@schematichq/schematic-js 1.2.12 → 1.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- "use strict";(()=>{var re=Object.create;var Q=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ae=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var ce=(s,e)=>()=>(e||s((e={exports:{}}).exports,e),e.exports);var le=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of se(e))!oe.call(s,n)&&n!==t&&Q(s,n,{get:()=>e[n],enumerable:!(i=ie(e,n))||i.enumerable});return s};var ue=(s,e,t)=>(t=s!=null?re(ae(s)):{},le(e||!s||!s.__esModule?Q(t,"default",{value:s,enumerable:!0}):t,s));var G=ce(z=>{(function(s){var e=(function(t){var i=typeof globalThis<"u"&&globalThis||typeof s<"u"&&s||typeof global<"u"&&global||{},n={searchParams:"URLSearchParams"in i,iterable:"Symbol"in i&&"iterator"in Symbol,blob:"FileReader"in i&&"Blob"in i&&(function(){try{return new Blob,!0}catch{return!1}})(),formData:"FormData"in i,arrayBuffer:"ArrayBuffer"in i};function c(r){return r&&DataView.prototype.isPrototypeOf(r)}if(n.arrayBuffer)var o=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],l=ArrayBuffer.isView||function(r){return r&&o.indexOf(Object.prototype.toString.call(r))>-1};function d(r){if(typeof r!="string"&&(r=String(r)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(r)||r==="")throw new TypeError('Invalid character in header field name: "'+r+'"');return r.toLowerCase()}function p(r){return typeof r!="string"&&(r=String(r)),r}function E(r){var a={next:function(){var u=r.shift();return{done:u===void 0,value:u}}};return n.iterable&&(a[Symbol.iterator]=function(){return a}),a}function g(r){this.map={},r instanceof g?r.forEach(function(a,u){this.append(u,a)},this):Array.isArray(r)?r.forEach(function(a){if(a.length!=2)throw new TypeError("Headers constructor: expected name/value pair to be length 2, found"+a.length);this.append(a[0],a[1])},this):r&&Object.getOwnPropertyNames(r).forEach(function(a){this.append(a,r[a])},this)}g.prototype.append=function(r,a){r=d(r),a=p(a);var u=this.map[r];this.map[r]=u?u+", "+a:a},g.prototype.delete=function(r){delete this.map[d(r)]},g.prototype.get=function(r){return r=d(r),this.has(r)?this.map[r]:null},g.prototype.has=function(r){return this.map.hasOwnProperty(d(r))},g.prototype.set=function(r,a){this.map[d(r)]=p(a)},g.prototype.forEach=function(r,a){for(var u in this.map)this.map.hasOwnProperty(u)&&r.call(a,this.map[u],u,this)},g.prototype.keys=function(){var r=[];return this.forEach(function(a,u){r.push(u)}),E(r)},g.prototype.values=function(){var r=[];return this.forEach(function(a){r.push(a)}),E(r)},g.prototype.entries=function(){var r=[];return this.forEach(function(a,u){r.push([u,a])}),E(r)},n.iterable&&(g.prototype[Symbol.iterator]=g.prototype.entries);function k(r){if(!r._noBody){if(r.bodyUsed)return Promise.reject(new TypeError("Already read"));r.bodyUsed=!0}}function x(r){return new Promise(function(a,u){r.onload=function(){a(r.result)},r.onerror=function(){u(r.error)}})}function T(r){var a=new FileReader,u=x(a);return a.readAsArrayBuffer(r),u}function V(r){var a=new FileReader,u=x(a),h=/charset=([A-Za-z0-9_-]+)/.exec(r.type),y=h?h[1]:"utf-8";return a.readAsText(r,y),u}function Y(r){for(var a=new Uint8Array(r),u=new Array(a.length),h=0;h<a.length;h++)u[h]=String.fromCharCode(a[h]);return u.join("")}function W(r){if(r.slice)return r.slice(0);var a=new Uint8Array(r.byteLength);return a.set(new Uint8Array(r)),a.buffer}function J(){return this.bodyUsed=!1,this._initBody=function(r){this.bodyUsed=this.bodyUsed,this._bodyInit=r,r?typeof r=="string"?this._bodyText=r:n.blob&&Blob.prototype.isPrototypeOf(r)?this._bodyBlob=r:n.formData&&FormData.prototype.isPrototypeOf(r)?this._bodyFormData=r:n.searchParams&&URLSearchParams.prototype.isPrototypeOf(r)?this._bodyText=r.toString():n.arrayBuffer&&n.blob&&c(r)?(this._bodyArrayBuffer=W(r.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):n.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(r)||l(r))?this._bodyArrayBuffer=W(r):this._bodyText=r=Object.prototype.toString.call(r):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof r=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n.searchParams&&URLSearchParams.prototype.isPrototypeOf(r)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},n.blob&&(this.blob=function(){var r=k(this);if(r)return r;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))}),this.arrayBuffer=function(){if(this._bodyArrayBuffer){var r=k(this);return r||(ArrayBuffer.isView(this._bodyArrayBuffer)?Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset,this._bodyArrayBuffer.byteOffset+this._bodyArrayBuffer.byteLength)):Promise.resolve(this._bodyArrayBuffer))}else{if(n.blob)return this.blob().then(T);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var r=k(this);if(r)return r;if(this._bodyBlob)return V(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(Y(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},n.formData&&(this.formData=function(){return this.text().then(ee)}),this.json=function(){return this.text().then(JSON.parse)},this}var Z=["CONNECT","DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT","TRACE"];function j(r){var a=r.toUpperCase();return Z.indexOf(a)>-1?a:r}function R(r,a){if(!(this instanceof R))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');a=a||{};var u=a.body;if(r instanceof R){if(r.bodyUsed)throw new TypeError("Already read");this.url=r.url,this.credentials=r.credentials,a.headers||(this.headers=new g(r.headers)),this.method=r.method,this.mode=r.mode,this.signal=r.signal,!u&&r._bodyInit!=null&&(u=r._bodyInit,r.bodyUsed=!0)}else this.url=String(r);if(this.credentials=a.credentials||this.credentials||"same-origin",(a.headers||!this.headers)&&(this.headers=new g(a.headers)),this.method=j(a.method||this.method||"GET"),this.mode=a.mode||this.mode||null,this.signal=a.signal||this.signal||(function(){if("AbortController"in i){var f=new AbortController;return f.signal}})(),this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&u)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(u),(this.method==="GET"||this.method==="HEAD")&&(a.cache==="no-store"||a.cache==="no-cache")){var h=/([?&])_=[^&]*/;if(h.test(this.url))this.url=this.url.replace(h,"$1_="+new Date().getTime());else{var y=/\?/;this.url+=(y.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}R.prototype.clone=function(){return new R(this,{body:this._bodyInit})};function ee(r){var a=new FormData;return r.trim().split("&").forEach(function(u){if(u){var h=u.split("="),y=h.shift().replace(/\+/g," "),f=h.join("=").replace(/\+/g," ");a.append(decodeURIComponent(y),decodeURIComponent(f))}}),a}function te(r){var a=new g,u=r.replace(/\r?\n[\t ]+/g," ");return u.split("\r").map(function(h){return h.indexOf(`
2
- `)===0?h.substr(1,h.length):h}).forEach(function(h){var y=h.split(":"),f=y.shift().trim();if(f){var D=y.join(":").trim();try{a.append(f,D)}catch(A){console.warn("Response "+A.message)}}}),a}J.call(R.prototype);function v(r,a){if(!(this instanceof v))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(a||(a={}),this.type="default",this.status=a.status===void 0?200:a.status,this.status<200||this.status>599)throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].");this.ok=this.status>=200&&this.status<300,this.statusText=a.statusText===void 0?"":""+a.statusText,this.headers=new g(a.headers),this.url=a.url||"",this._initBody(r)}J.call(v.prototype),v.prototype.clone=function(){return new v(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new g(this.headers),url:this.url})},v.error=function(){var r=new v(null,{status:200,statusText:""});return r.ok=!1,r.status=0,r.type="error",r};var ne=[301,302,303,307,308];v.redirect=function(r,a){if(ne.indexOf(a)===-1)throw new RangeError("Invalid status code");return new v(null,{status:a,headers:{location:r}})},t.DOMException=i.DOMException;try{new t.DOMException}catch{t.DOMException=function(a,u){this.message=a,this.name=u;var h=Error(a);this.stack=h.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function I(r,a){return new Promise(function(u,h){var y=new R(r,a);if(y.signal&&y.signal.aborted)return h(new t.DOMException("Aborted","AbortError"));var f=new XMLHttpRequest;function D(){f.abort()}f.onload=function(){var m={statusText:f.statusText,headers:te(f.getAllResponseHeaders()||"")};y.url.indexOf("file://")===0&&(f.status<200||f.status>599)?m.status=200:m.status=f.status,m.url="responseURL"in f?f.responseURL:m.headers.get("X-Request-URL");var S="response"in f?f.response:f.responseText;setTimeout(function(){u(new v(S,m))},0)},f.onerror=function(){setTimeout(function(){h(new TypeError("Network request failed"))},0)},f.ontimeout=function(){setTimeout(function(){h(new TypeError("Network request timed out"))},0)},f.onabort=function(){setTimeout(function(){h(new t.DOMException("Aborted","AbortError"))},0)};function A(m){try{return m===""&&i.location.href?i.location.href:m}catch{return m}}if(f.open(y.method,A(y.url),!0),y.credentials==="include"?f.withCredentials=!0:y.credentials==="omit"&&(f.withCredentials=!1),"responseType"in f&&(n.blob?f.responseType="blob":n.arrayBuffer&&(f.responseType="arraybuffer")),a&&typeof a.headers=="object"&&!(a.headers instanceof g||i.Headers&&a.headers instanceof i.Headers)){var q=[];Object.getOwnPropertyNames(a.headers).forEach(function(m){q.push(d(m)),f.setRequestHeader(m,p(a.headers[m]))}),y.headers.forEach(function(m,S){q.indexOf(S)===-1&&f.setRequestHeader(S,m)})}else y.headers.forEach(function(m,S){f.setRequestHeader(S,m)});y.signal&&(y.signal.addEventListener("abort",D),f.onreadystatechange=function(){f.readyState===4&&y.signal.removeEventListener("abort",D)}),f.send(typeof y._bodyInit>"u"?null:y._bodyInit)})}return I.polyfill=!0,i.fetch||(i.fetch=I,i.Headers=g,i.Request=R,i.Response=v),t.Headers=g,t.Request=R,t.Response=v,t.fetch=I,t})({})})(typeof self<"u"?self:z)});var b=[];for(let s=0;s<256;++s)b.push((s+256).toString(16).slice(1));function H(s,e=0){return(b[s[e+0]]+b[s[e+1]]+b[s[e+2]]+b[s[e+3]]+"-"+b[s[e+4]]+b[s[e+5]]+"-"+b[s[e+6]]+b[s[e+7]]+"-"+b[s[e+8]]+b[s[e+9]]+"-"+b[s[e+10]]+b[s[e+11]]+b[s[e+12]]+b[s[e+13]]+b[s[e+14]]+b[s[e+15]]).toLowerCase()}var P,de=new Uint8Array(16);function N(){if(!P){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");P=crypto.getRandomValues.bind(crypto)}return P(de)}var fe=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),B={randomUUID:fe};function he(s,e,t){s=s||{};let i=s.random??s.rng?.()??N();if(i.length<16)throw new Error("Random bytes length must be >= 16");if(i[6]=i[6]&15|64,i[8]=i[8]&63|128,e){if(t=t||0,t<0||t+16>e.length)throw new RangeError(`UUID byte range ${t}:${t+15} is out of buffer bounds`);for(let n=0;n<16;++n)e[t+n]=i[n];return e}return H(i)}function ge(s,e,t){return B.randomUUID&&!e&&!s?B.randomUUID():he(s,e,t)}var C=ge;var it=ue(G());function F(s){return pe(s,!1)}function pe(s,e){return s==null?s:{companyId:s.company_id==null?void 0:s.company_id,error:s.error==null?void 0:s.error,featureAllocation:s.feature_allocation==null?void 0:s.feature_allocation,featureUsage:s.feature_usage==null?void 0:s.feature_usage,featureUsageEvent:s.feature_usage_event==null?void 0:s.feature_usage_event,featureUsagePeriod:s.feature_usage_period==null?void 0:s.feature_usage_period,featureUsageResetAt:s.feature_usage_reset_at==null?void 0:new Date(s.feature_usage_reset_at),flag:s.flag,flagId:s.flag_id==null?void 0:s.flag_id,reason:s.reason,ruleId:s.rule_id==null?void 0:s.rule_id,ruleType:s.rule_type==null?void 0:s.rule_type,userId:s.user_id==null?void 0:s.user_id,value:s.value}}function L(s){return ye(s,!1)}function ye(s,e=!1){return s==null?s:{company_id:s.companyId,error:s.error,flag_id:s.flagId,flag_key:s.flagKey,reason:s.reason,req_company:s.reqCompany,req_user:s.reqUser,rule_id:s.ruleId,user_id:s.userId,value:s.value}}function _(s){return be(s,!1)}function be(s,e){return s==null?s:{data:F(s.data),params:s.params}}function K(s){return ve(s,!1)}function ve(s,e){return s==null?s:{flags:s.flags.map(F)}}function M(s){return ke(s,!1)}function ke(s,e){return s==null?s:{data:K(s.data),params:s.params}}var O=s=>{let{companyId:e,error:t,featureAllocation:i,featureUsage:n,featureUsageEvent:c,featureUsagePeriod:o,featureUsageResetAt:l,flag:d,flagId:p,reason:E,ruleId:g,ruleType:k,userId:x,value:T}=F(s);return{featureUsageExceeded:!T&&(k=="company_override_usage_exceeded"||k=="plan_entitlement_usage_exceeded"),companyId:e??void 0,error:t??void 0,featureAllocation:i??void 0,featureUsage:n??void 0,featureUsageEvent:c===null?void 0:c,featureUsagePeriod:o??void 0,featureUsageResetAt:l??void 0,flag:d,flagId:p??void 0,reason:E,ruleId:g??void 0,ruleType:k??void 0,userId:x??void 0,value:T}};function w(s){let e=Object.keys(s).reduce((t,i)=>{let c=Object.keys(s[i]||{}).sort().reduce((o,l)=>(o[l]=s[i][l],o),{});return t[i]=c,t},{});return JSON.stringify(e)}var $="1.2.12";var X="schematicId";var U=class{additionalHeaders={};apiKey;apiUrl="https://api.schematichq.com";conn=null;context={};debugEnabled=!1;offlineEnabled=!1;eventQueue;contextDependentEventQueue;eventUrl="https://c.schematichq.com";flagCheckListeners={};flagValueListeners={};isPending=!0;isPendingListeners=new Set;storage;useWebSocket=!1;checks={};featureUsageEventMap={};webSocketUrl="wss://api.schematichq.com";webSocketConnectionTimeout=1e4;webSocketReconnect=!0;webSocketMaxReconnectAttempts=7;webSocketInitialRetryDelay=1e3;webSocketMaxRetryDelay=3e4;wsReconnectAttempts=0;wsReconnectTimer=null;wsIntentionalDisconnect=!1;currentWebSocket=null;isConnecting=!1;maxEventQueueSize=100;maxEventRetries=5;eventRetryInitialDelay=1e3;eventRetryMaxDelay=3e4;retryTimer=null;flagValueDefaults={};flagCheckDefaults={};constructor(e,t){if(this.apiKey=e,this.eventQueue=[],this.contextDependentEventQueue=[],this.useWebSocket=t?.useWebSocket??!1,this.debugEnabled=t?.debug??!1,this.offlineEnabled=t?.offline??!1,typeof window<"u"&&typeof window.location<"u"){let i=new URLSearchParams(window.location.search),n=i.get("schematic_debug");n!==null&&(n===""||n==="true"||n==="1")&&(this.debugEnabled=!0);let c=i.get("schematic_offline");c!==null&&(c===""||c==="true"||c==="1")&&(this.offlineEnabled=!0,this.debugEnabled=!0)}this.offlineEnabled&&t?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${$}`,...t?.additionalHeaders??{}},t?.storage?this.storage=t.storage:typeof localStorage<"u"&&(this.storage=localStorage),t?.apiUrl!==void 0&&(this.apiUrl=t.apiUrl),t?.eventUrl!==void 0&&(this.eventUrl=t.eventUrl),t?.webSocketUrl!==void 0&&(this.webSocketUrl=t.webSocketUrl),t?.webSocketConnectionTimeout!==void 0&&(this.webSocketConnectionTimeout=t.webSocketConnectionTimeout),t?.webSocketReconnect!==void 0&&(this.webSocketReconnect=t.webSocketReconnect),t?.webSocketMaxReconnectAttempts!==void 0&&(this.webSocketMaxReconnectAttempts=t.webSocketMaxReconnectAttempts),t?.webSocketInitialRetryDelay!==void 0&&(this.webSocketInitialRetryDelay=t.webSocketInitialRetryDelay),t?.webSocketMaxRetryDelay!==void 0&&(this.webSocketMaxRetryDelay=t.webSocketMaxRetryDelay),t?.maxEventQueueSize!==void 0&&(this.maxEventQueueSize=t.maxEventQueueSize),t?.maxEventRetries!==void 0&&(this.maxEventRetries=t.maxEventRetries),t?.eventRetryInitialDelay!==void 0&&(this.eventRetryInitialDelay=t.eventRetryInitialDelay),t?.eventRetryMaxDelay!==void 0&&(this.eventRetryMaxDelay=t.eventRetryMaxDelay),t?.flagValueDefaults!==void 0&&(this.flagValueDefaults=t.flagValueDefaults),t?.flagCheckDefaults!==void 0&&(this.flagCheckDefaults=t.flagCheckDefaults),typeof window<"u"&&window?.addEventListener&&(window.addEventListener("beforeunload",()=>{this.flushEventQueue(),this.flushContextDependentEventQueue()}),this.useWebSocket&&(window.addEventListener("offline",()=>{this.debug("Browser went offline, closing WebSocket connection"),this.handleNetworkOffline()}),window.addEventListener("online",()=>{this.debug("Browser came online, attempting to reconnect WebSocket"),this.handleNetworkOnline()}))),this.offlineEnabled?this.debug("Initialized with offline mode enabled - no network requests will be made"):this.debugEnabled&&this.debug("Initialized with debug mode enabled")}resolveFallbackValue(e,t){return t!==void 0?t:e in this.flagValueDefaults?this.flagValueDefaults[e]:!1}resolveFallbackCheckFlagReturn(e,t,i="Fallback value used",n){if(t!==void 0)return{flag:e,value:t,reason:i,error:n};if(e in this.flagCheckDefaults){let c=this.flagCheckDefaults[e];return{...c,flag:e,reason:n!==void 0?i:c.reason,error:n}}return e in this.flagValueDefaults?{flag:e,value:this.flagValueDefaults[e],reason:i,error:n}:{flag:e,value:!1,reason:i,error:n}}async checkFlag(e){let{fallback:t,key:i}=e,n=e.context||this.context,c=w(n);if(this.debug(`checkFlag: ${i}`,{context:n,fallback:t}),this.isOffline()){let o=this.resolveFallbackCheckFlagReturn(i,t,"Offline mode - using initialization defaults");return this.debug(`checkFlag offline result: ${i}`,{value:o.value,offlineMode:!0}),o.value}if(!this.useWebSocket){let o=`${this.apiUrl}/flags/${i}/check`;return fetch(o,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(n)}).then(l=>{if(!l.ok)throw new Error("Network response was not ok");return l.json()}).then(l=>{let d=_(l);this.debug(`checkFlag result: ${i}`,d);let p=O(d.data);return typeof p.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(p),this.submitFlagCheckEvent(i,p,n),p.value}).catch(l=>{console.error("There was a problem with the fetch operation:",l);let d=this.resolveFallbackCheckFlagReturn(i,t,"API request failed",l instanceof Error?l.message:String(l));return this.submitFlagCheckEvent(i,d,n),d.value})}try{let o=this.checks[c];if(this.conn!==null&&typeof o<"u"&&typeof o[i]<"u")return this.debug(`checkFlag cached result: ${i}`,o[i]),o[i].value;if(this.isOffline())return this.resolveFallbackValue(i,t);try{await this.setContext(n)}catch(E){return console.error("WebSocket connection failed, falling back to REST:",E),this.fallbackToRest(i,n,t)}let d=(this.checks[c]??{})[i],p=d?.value??this.resolveFallbackValue(i,t);return this.debug(`checkFlag WebSocket result: ${i}`,typeof d<"u"?d:{value:p,fallbackUsed:!0}),typeof d<"u"&&this.submitFlagCheckEvent(i,d,n),p}catch(o){console.error("Unexpected error in checkFlag:",o);let l=this.resolveFallbackCheckFlagReturn(i,t,"Unexpected error in flag check",o instanceof Error?o.message:String(o));return this.submitFlagCheckEvent(i,l,n),l.value}}debug(e,...t){this.debugEnabled&&console.log(`[Schematic] ${e}`,...t)}createPersistentMessageHandler(e){return t=>{let i=JSON.parse(t.data);this.debug("WebSocket persistent message received:",i),w(e)in this.checks||(this.checks[w(e)]={}),(i.flags??[]).forEach(n=>{let c=O(n),o=w(e);this.checks[o]===void 0&&(this.checks[o]={}),this.checks[o][c.flag]=c,this.debug("WebSocket flag update:",{flag:c.flag,value:c.value,flagCheck:c}),typeof c.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(c),((this.flagCheckListeners[n.flag]?.size??0)>0||(this.flagValueListeners[n.flag]?.size??0)>0)&&this.submitFlagCheckEvent(c.flag,c,e),this.debug(`About to notify listeners for flag ${n.flag}`,{flag:n.flag,value:c.value}),this.notifyFlagCheckListeners(n.flag,c),this.notifyFlagValueListeners(n.flag,c.value),this.debug(`Finished notifying listeners for flag ${n.flag}`,{flag:n.flag,value:c.value})}),this.flushContextDependentEventQueue(),this.setIsPending(!1)}}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(e,t,i){let n={flagKey:e,value:t.value,reason:t.reason,flagId:t.flagId,ruleId:t.ruleId,companyId:t.companyId,userId:t.userId,error:t.error,reqCompany:i.company,reqUser:i.user};return this.debug("submitting flag check event:",n),this.handleEvent("flag_check",L(n))}async fallbackToRest(e,t,i){if(this.isOffline()){let n=this.resolveFallbackValue(e,i);return this.debug(`fallbackToRest offline result: ${e}`,{value:n,offlineMode:!0}),n}try{let n=`${this.apiUrl}/flags/${e}/check`,c=await fetch(n,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(t)});if(!c.ok)throw new Error("Network response was not ok");let o=await c.json(),l=_(o);this.debug(`fallbackToRest result: ${e}`,l);let d=O(l.data);return typeof d.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(d),this.submitFlagCheckEvent(e,d,t),d.value}catch(n){console.error("REST API call failed, using fallback value:",n);let c=this.resolveFallbackCheckFlagReturn(e,i,"API request failed (fallback)",n instanceof Error?n.message:String(n));return this.submitFlagCheckEvent(e,c,t),c.value}}checkFlags=async e=>{if(e=e||this.context,this.debug("checkFlags",{context:e}),this.isOffline())return this.debug("checkFlags offline result: returning empty object"),{};let t=`${this.apiUrl}/flags/check`,i=JSON.stringify(e);return fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:i}).then(n=>{if(!n.ok)throw new Error("Network response was not ok");return n.json()}).then(n=>{let c=M(n);return this.debug("checkFlags result:",c),(c?.data?.flags??[]).reduce((o,l)=>(o[l.flag]=l.value,o),{})}).catch(n=>(console.error("There was a problem with the fetch operation:",n),{}))};identify=e=>{this.debug("identify:",e);try{this.setContext({company:e.company?.keys,user:e.keys})}catch(t){console.error("Error setting context:",t)}return this.handleEvent("identify",e)};setContext=async e=>{if(this.isOffline()||!this.useWebSocket)return this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),Promise.resolve();try{if(this.setIsPending(!0),!this.conn){if(this.isConnecting){for(this.debug("Connection already in progress, waiting for it to complete");this.isConnecting&&this.conn===null;)await new Promise(i=>setTimeout(i,10));if(this.conn!==null){let i=await this.conn;await this.wsSendMessage(i,e);return}}this.wsReconnectTimer!==null&&(this.debug("Cancelling scheduled reconnection, connecting immediately"),clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.isConnecting=!0;try{this.conn=this.wsConnect();let i=await this.conn;this.isConnecting=!1,await this.wsSendMessage(i,e);return}catch(i){throw this.isConnecting=!1,i}}let t=await this.conn;await this.wsSendMessage(t,e)}catch(t){throw console.error("Failed to establish WebSocket connection:",t),t}};track=e=>{let{company:t,user:i,event:n,traits:c,quantity:o=1}=e;if(!this.hasContext(t,i)){this.debug(`track: queuing event "${n}" until context is available`);let d={api_key:this.apiKey,body:{company:t,event:n,traits:c??{},user:i,quantity:o},sent_at:new Date().toISOString(),tracker_event_id:C(),tracker_user_id:this.getAnonymousId(),type:"track"};return this.contextDependentEventQueue.push(d),Promise.resolve()}let l={company:t??this.context.company,event:n,traits:c??{},user:i??this.context.user,quantity:o};return this.debug("track:",l),n in this.featureUsageEventMap&&this.optimisticallyUpdateFeatureUsage(n,o),this.handleEvent("track",l)};optimisticallyUpdateFeatureUsage=(e,t=1)=>{let i=this.featureUsageEventMap[e];i!=null&&(this.debug(`Optimistically updating feature usage for event: ${e}`,{quantity:t}),Object.entries(i).forEach(([n,c])=>{if(c===void 0)return;let o={...c};if(typeof o.featureUsage=="number"){if(o.featureUsage+=t,typeof o.featureAllocation=="number"){let d=o.featureUsageExceeded===!0,p=o.featureUsage>=o.featureAllocation;p!==d&&(o.featureUsageExceeded=p,p&&(o.value=!1),this.debug(`Usage limit status changed for flag: ${n}`,{was:d?"exceeded":"within limits",now:p?"exceeded":"within limits",featureUsage:o.featureUsage,featureAllocation:o.featureAllocation,value:o.value}))}this.featureUsageEventMap[e]!==void 0&&(this.featureUsageEventMap[e][n]=o);let l=w(this.context);this.checks[l]!==void 0&&this.checks[l]!==null&&(this.checks[l][n]=o),this.notifyFlagCheckListeners(n,o),this.notifyFlagValueListeners(n,o.value)}}))};hasContext=(e,t)=>{let i=e!=null&&Object.keys(e).length>0||t!=null&&Object.keys(t).length>0,n=this.context.company!==void 0&&this.context.company!==null&&Object.keys(this.context.company).length>0||this.context.user!==void 0&&this.context.user!==null&&Object.keys(this.context.user).length>0;return i||n};flushContextDependentEventQueue=()=>{for(this.debug(`flushing ${this.contextDependentEventQueue.length} context-dependent events`);this.contextDependentEventQueue.length>0;){let e=this.contextDependentEventQueue.shift();if(e)if(e.type==="track"&&typeof e.body=="object"&&e.body!==null){let t=e.body,i={...t,company:t.company??this.context.company,user:t.user??this.context.user},n={...e,body:i,sent_at:new Date().toISOString()};this.sendEvent(n)}else this.sendEvent(e)}};startRetryTimer=()=>{this.retryTimer===null&&(this.retryTimer=setInterval(()=>{this.flushEventQueue().catch(e=>{this.debug("Error in retry timer flush:",e)}),this.eventQueue.length===0&&this.stopRetryTimer()},5e3),this.debug("Started retry timer"))};stopRetryTimer=()=>{this.retryTimer!==null&&(clearInterval(this.retryTimer),this.retryTimer=null,this.debug("Stopped retry timer"))};flushEventQueue=async()=>{if(this.eventQueue.length===0)return;let e=Date.now(),t=[],i=[];for(let n of this.eventQueue)n.next_retry_at===void 0||n.next_retry_at<=e?t.push(n):i.push(n);if(t.length===0){this.debug(`No events ready for retry yet (${i.length} still in backoff)`);return}this.debug(`Flushing event queue: ${t.length} ready, ${i.length} waiting`),this.eventQueue=i;for(let n of t)try{await this.sendEvent(n),this.debug("Queued event sent successfully:",n.type)}catch(c){this.debug("Failed to send queued event:",c)}};getAnonymousId=()=>{if(!this.storage)return C();let e=this.storage.getItem(X);if(typeof e<"u")return e;let t=C();return this.storage.setItem(X,t),t};handleEvent=(e,t)=>{let i={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:C(),tracker_user_id:this.getAnonymousId(),type:e};return typeof document<"u"&&document?.hidden?this.storeEvent(i):this.sendEvent(i)};sendEvent=async e=>{let t=`${this.eventUrl}/e`,i=JSON.stringify(e);if(this.debug("sending event:",{url:t,event:e}),this.isOffline())return this.debug("event not sent (offline mode):",{event:e}),Promise.resolve();try{let n=await fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:i});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`);this.debug("event sent:",{status:n.status,statusText:n.statusText})}catch(n){let c=(e.retry_count??0)+1;if(c<=this.maxEventRetries){this.debug(`Event failed to send (attempt ${c}/${this.maxEventRetries}), queueing for retry:`,n);let o=this.eventRetryInitialDelay*Math.pow(2,c-1),l=Math.min(o,this.eventRetryMaxDelay),d=Date.now()+l,p={...e,retry_count:c,next_retry_at:d};this.eventQueue.length<this.maxEventQueueSize?(this.eventQueue.push(p),this.debug(`Event queued for retry in ${l}ms (${this.eventQueue.length}/${this.maxEventQueueSize})`)):(this.debug(`Event queue full (${this.maxEventQueueSize}), dropping oldest event`),this.eventQueue.shift(),this.eventQueue.push(p)),this.startRetryTimer()}else this.debug(`Event failed permanently after ${this.maxEventRetries} attempts, dropping:`,n)}return Promise.resolve()};storeEvent=e=>(this.eventQueue.push(e),Promise.resolve());forceReconnect=async()=>this.reconnect({force:!0});reconnectIfNeeded=async()=>this.reconnect({force:!1});reconnect=async e=>{let{force:t}=e,i=t?"forceReconnect":"reconnectIfNeeded";if(this.isOffline())return this.debug(`${i}: skipped (offline mode)`),Promise.resolve();if(!t&&this.conn!==null)try{if((await this.conn).readyState===WebSocket.OPEN)return this.debug(`${i}: connection is healthy, skipping`),Promise.resolve()}catch{}if(this.debug(`${i}: ${t?"forcing immediate reconnection":"reconnecting"}`),this.wsIntentionalDisconnect=!1,this.wsReconnectTimer!==null&&(this.debug(`${i}: cancelling pending reconnection timer`),clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.wsReconnectAttempts=0,this.conn!==null){this.debug(`${i}: closing existing connection`);try{let n=await this.conn;this.currentWebSocket===n&&(this.currentWebSocket=null),(n.readyState===WebSocket.OPEN||n.readyState===WebSocket.CONNECTING)&&n.close()}catch(n){this.debug(`${i}: error closing existing connection:`,n)}this.conn=null,this.isConnecting=!1}if(this.context.company!==void 0||this.context.user!==void 0){this.debug(`${i}: reconnecting with existing context`);try{this.isConnecting=!0,this.conn=this.wsConnect();let n=await this.conn;this.isConnecting=!1,await this.wsSendMessage(n,this.context,!0),this.debug(`${i}: reconnection successful`)}catch(n){this.isConnecting=!1,this.debug(`${i}: reconnection failed:`,n)}}else this.debug(`${i}: no context set, skipping reconnection`);return Promise.resolve()};cleanup=async()=>{if(this.isOffline())return this.debug("cleanup: skipped (offline mode)"),Promise.resolve();if(this.wsIntentionalDisconnect=!0,this.wsReconnectTimer!==null&&(clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.stopRetryTimer(),this.conn)try{let e=await this.conn;this.currentWebSocket===e&&(this.debug("Cleaning up current websocket tracking"),this.currentWebSocket=null),e.close()}catch(e){console.error("Error during cleanup:",e)}finally{this.conn=null,this.currentWebSocket=null,this.isConnecting=!1}};calculateReconnectDelay=()=>{let e=this.webSocketInitialRetryDelay*Math.pow(2,this.wsReconnectAttempts),t=Math.min(e,this.webSocketMaxRetryDelay),i=Math.random()*t*.5,n=t+i;return this.debug(`Reconnect delay calculated: ${n.toFixed(0)}ms (attempt ${this.wsReconnectAttempts+1}/${this.webSocketMaxReconnectAttempts})`),n};handleNetworkOffline=async()=>{if(this.conn!==null){try{let e=await this.conn;(e.readyState===WebSocket.OPEN||e.readyState===WebSocket.CONNECTING)&&e.close()}catch(e){this.debug("Error closing connection on offline:",e)}this.conn=null}this.wsReconnectTimer!==null&&(clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null)};handleNetworkOnline=()=>{this.debug("Network online, attempting reconnection and flushing queued events"),this.wsReconnectAttempts=0,this.wsReconnectTimer!==null&&(clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.flushEventQueue().catch(e=>{this.debug("Error flushing event queue on network online:",e)}),this.attemptReconnect()};attemptReconnect=()=>{if(this.wsReconnectAttempts>=this.webSocketMaxReconnectAttempts){this.debug(`Maximum reconnection attempts (${this.webSocketMaxReconnectAttempts}) reached, giving up`);return}if(this.wsReconnectTimer!==null){this.debug("Reconnection attempt already scheduled, ignoring duplicate request");return}let e=this.calculateReconnectDelay();this.debug(`Scheduling reconnection attempt ${this.wsReconnectAttempts+1}/${this.webSocketMaxReconnectAttempts} in ${e.toFixed(0)}ms`),this.wsReconnectTimer=setTimeout(async()=>{this.wsReconnectTimer=null,this.wsReconnectAttempts++,this.debug(`Attempting to reconnect (attempt ${this.wsReconnectAttempts}/${this.webSocketMaxReconnectAttempts})`);try{if(this.conn!==null){this.debug("Cleaning up existing connection before reconnection");try{let t=await this.conn;this.currentWebSocket===t&&(this.debug("Existing websocket is current, will be replaced"),this.currentWebSocket=null),(t.readyState===WebSocket.OPEN||t.readyState===WebSocket.CONNECTING)&&t.close()}catch(t){this.debug("Error cleaning up existing connection:",t)}this.conn=null,this.currentWebSocket=null,this.isConnecting=!1}this.isConnecting=!0;try{this.conn=this.wsConnect();let t=await this.conn;this.isConnecting=!1,this.debug("Reconnection context check:",{hasCompany:this.context.company!==void 0,hasUser:this.context.user!==void 0,context:this.context}),this.context.company!==void 0||this.context.user!==void 0?(this.debug("Reconnected, force re-sending context"),await this.wsSendMessage(t,this.context,!0)):(this.debug("No context to re-send after reconnection - websocket ready for new context"),this.debug("Setting up tracking for reconnected websocket (no context to send)"),this.currentWebSocket=t),this.flushEventQueue().catch(i=>{this.debug("Error flushing event queue after websocket reconnection:",i)}),this.debug("Reconnection successful")}catch(t){throw this.isConnecting=!1,t}}catch(t){this.debug("Reconnection attempt failed:",t)}},e)};wsConnect=()=>this.isOffline()?(this.debug("wsConnect: skipped (offline mode)"),Promise.reject(new Error("WebSocket connection skipped in offline mode"))):new Promise((e,t)=>{let i=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",i);let n=new WebSocket(i),c=Math.random().toString(36).substring(7);this.debug(`Creating WebSocket connection ${c} to ${i}`);let o=null,l=!1;o=setTimeout(()=>{l||(this.debug(`WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`),n.close(),t(new Error("WebSocket connection timeout")))},this.webSocketConnectionTimeout),n.onopen=()=>{l=!0,o!==null&&clearTimeout(o),this.wsReconnectAttempts=0,this.wsIntentionalDisconnect=!1,this.debug(`WebSocket connection ${c} opened successfully`),e(n)},n.onerror=d=>{l=!0,o!==null&&clearTimeout(o),this.debug(`WebSocket connection ${c} error:`,d),t(d)},n.onclose=()=>{l=!0,o!==null&&clearTimeout(o),this.debug(`WebSocket connection ${c} closed`),this.conn=null,this.currentWebSocket===n&&(this.currentWebSocket=null,this.isConnecting=!1),!this.wsIntentionalDisconnect&&this.webSocketReconnect&&this.attemptReconnect()}});wsSendMessage=(e,t,i=!1)=>this.isOffline()?(this.debug("wsSendMessage: skipped (offline mode)"),this.setIsPending(!1),Promise.resolve()):new Promise((n,c)=>{if(!i&&w(t)==w(this.context))return this.debug("WebSocket context unchanged, skipping update"),n(this.setIsPending(!1));this.debug(i?"WebSocket force sending context (reconnection):":"WebSocket context updated:",t),this.context=t;let o=()=>{let l=!1,d=this.createPersistentMessageHandler(t),p=k=>{d(k),l||(l=!0,n())};e.addEventListener("message",p),this.currentWebSocket=e;let E=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${$}`,g={apiKey:this.apiKey,clientVersion:E,data:t};this.debug("WebSocket sending message:",g),e.send(JSON.stringify(g))};e.readyState===WebSocket.OPEN?(this.debug("WebSocket already open, sending message"),o()):e.readyState===WebSocket.CONNECTING?(this.debug("WebSocket connecting, waiting for open to send message"),e.addEventListener("open",o)):(this.debug("WebSocket is closed, cannot send message"),c("WebSocket is not open or connecting"))});getIsPending=()=>this.isPending;addIsPendingListener=e=>(this.isPendingListeners.add(e),()=>{this.isPendingListeners.delete(e)});setIsPending=e=>{this.isPending=e,this.isPendingListeners.forEach(t=>Ee(t,e))};getFlagCheck=e=>{let t=w(this.context);return(this.checks[t]??{})[e]};getFlagValue=e=>this.getFlagCheck(e)?.value;addFlagValueListener=(e,t)=>(e in this.flagValueListeners||(this.flagValueListeners[e]=new Set),this.flagValueListeners[e].add(t),()=>{this.flagValueListeners[e].delete(t)});addFlagCheckListener=(e,t)=>(e in this.flagCheckListeners||(this.flagCheckListeners[e]=new Set),this.flagCheckListeners[e].add(t),()=>{this.flagCheckListeners[e].delete(t)});notifyFlagCheckListeners=(e,t)=>{let i=this.flagCheckListeners?.[e]??[];i.size>0&&this.debug(`Notifying ${i.size} flag check listeners for ${e}`,t),typeof t.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(t),i.forEach(n=>we(n,t))};updateFeatureUsageEventMap=e=>{if(typeof e.featureUsageEvent!="string")return;let t=e.featureUsageEvent;(this.featureUsageEventMap[t]===void 0||this.featureUsageEventMap[t]===null)&&(this.featureUsageEventMap[t]={}),this.featureUsageEventMap[t]!==void 0&&(this.featureUsageEventMap[t][e.flag]=e),this.debug(`Updated featureUsageEventMap for event: ${t}, flag: ${e.flag}`,e)};notifyFlagValueListeners=(e,t)=>{let i=this.flagValueListeners?.[e]??[];i.size>0&&this.debug(`Notifying ${i.size} flag value listeners for ${e}`,{value:t}),i.forEach((n,c)=>{this.debug(`Calling listener ${c} for flag ${e}`,{flagKey:e,value:t}),Re(n,t),this.debug(`Listener ${c} for flag ${e} completed`,{flagKey:e,value:t})})}},Ee=(s,e)=>{s.length>0?s(e):s()},we=(s,e)=>{s.length>0?s(e):s()},Re=(s,e)=>{s.length>0?s(e):s()};window.Schematic=U;})();
1
+ "use strict";(()=>{var re=Object.create;var Q=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ae=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var ce=(s,e)=>()=>(e||s((e={exports:{}}).exports,e),e.exports);var le=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of se(e))!oe.call(s,n)&&n!==t&&Q(s,n,{get:()=>e[n],enumerable:!(r=ie(e,n))||r.enumerable});return s};var ue=(s,e,t)=>(t=s!=null?re(ae(s)):{},le(e||!s||!s.__esModule?Q(t,"default",{value:s,enumerable:!0}):t,s));var G=ce(z=>{(function(s){var e=(function(t){var r=typeof globalThis<"u"&&globalThis||typeof s<"u"&&s||typeof global<"u"&&global||{},n={searchParams:"URLSearchParams"in r,iterable:"Symbol"in r&&"iterator"in Symbol,blob:"FileReader"in r&&"Blob"in r&&(function(){try{return new Blob,!0}catch{return!1}})(),formData:"FormData"in r,arrayBuffer:"ArrayBuffer"in r};function c(i){return i&&DataView.prototype.isPrototypeOf(i)}if(n.arrayBuffer)var o=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],l=ArrayBuffer.isView||function(i){return i&&o.indexOf(Object.prototype.toString.call(i))>-1};function u(i){if(typeof i!="string"&&(i=String(i)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(i)||i==="")throw new TypeError('Invalid character in header field name: "'+i+'"');return i.toLowerCase()}function h(i){return typeof i!="string"&&(i=String(i)),i}function v(i){var a={next:function(){var d=i.shift();return{done:d===void 0,value:d}}};return n.iterable&&(a[Symbol.iterator]=function(){return a}),a}function p(i){this.map={},i instanceof p?i.forEach(function(a,d){this.append(d,a)},this):Array.isArray(i)?i.forEach(function(a){if(a.length!=2)throw new TypeError("Headers constructor: expected name/value pair to be length 2, found"+a.length);this.append(a[0],a[1])},this):i&&Object.getOwnPropertyNames(i).forEach(function(a){this.append(a,i[a])},this)}p.prototype.append=function(i,a){i=u(i),a=h(a);var d=this.map[i];this.map[i]=d?d+", "+a:a},p.prototype.delete=function(i){delete this.map[u(i)]},p.prototype.get=function(i){return i=u(i),this.has(i)?this.map[i]:null},p.prototype.has=function(i){return this.map.hasOwnProperty(u(i))},p.prototype.set=function(i,a){this.map[u(i)]=h(a)},p.prototype.forEach=function(i,a){for(var d in this.map)this.map.hasOwnProperty(d)&&i.call(a,this.map[d],d,this)},p.prototype.keys=function(){var i=[];return this.forEach(function(a,d){i.push(d)}),v(i)},p.prototype.values=function(){var i=[];return this.forEach(function(a){i.push(a)}),v(i)},p.prototype.entries=function(){var i=[];return this.forEach(function(a,d){i.push([d,a])}),v(i)},n.iterable&&(p.prototype[Symbol.iterator]=p.prototype.entries);function E(i){if(!i._noBody){if(i.bodyUsed)return Promise.reject(new TypeError("Already read"));i.bodyUsed=!0}}function x(i){return new Promise(function(a,d){i.onload=function(){a(i.result)},i.onerror=function(){d(i.error)}})}function T(i){var a=new FileReader,d=x(a);return a.readAsArrayBuffer(i),d}function W(i){var a=new FileReader,d=x(a),g=/charset=([A-Za-z0-9_-]+)/.exec(i.type),y=g?g[1]:"utf-8";return a.readAsText(i,y),d}function Y(i){for(var a=new Uint8Array(i),d=new Array(a.length),g=0;g<a.length;g++)d[g]=String.fromCharCode(a[g]);return d.join("")}function V(i){if(i.slice)return i.slice(0);var a=new Uint8Array(i.byteLength);return a.set(new Uint8Array(i)),a.buffer}function J(){return this.bodyUsed=!1,this._initBody=function(i){this.bodyUsed=this.bodyUsed,this._bodyInit=i,i?typeof i=="string"?this._bodyText=i:n.blob&&Blob.prototype.isPrototypeOf(i)?this._bodyBlob=i:n.formData&&FormData.prototype.isPrototypeOf(i)?this._bodyFormData=i:n.searchParams&&URLSearchParams.prototype.isPrototypeOf(i)?this._bodyText=i.toString():n.arrayBuffer&&n.blob&&c(i)?(this._bodyArrayBuffer=V(i.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):n.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(i)||l(i))?this._bodyArrayBuffer=V(i):this._bodyText=i=Object.prototype.toString.call(i):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof i=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n.searchParams&&URLSearchParams.prototype.isPrototypeOf(i)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},n.blob&&(this.blob=function(){var i=E(this);if(i)return i;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))}),this.arrayBuffer=function(){if(this._bodyArrayBuffer){var i=E(this);return i||(ArrayBuffer.isView(this._bodyArrayBuffer)?Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset,this._bodyArrayBuffer.byteOffset+this._bodyArrayBuffer.byteLength)):Promise.resolve(this._bodyArrayBuffer))}else{if(n.blob)return this.blob().then(T);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var i=E(this);if(i)return i;if(this._bodyBlob)return W(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(Y(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},n.formData&&(this.formData=function(){return this.text().then(ee)}),this.json=function(){return this.text().then(JSON.parse)},this}var Z=["CONNECT","DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT","TRACE"];function j(i){var a=i.toUpperCase();return Z.indexOf(a)>-1?a:i}function R(i,a){if(!(this instanceof R))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');a=a||{};var d=a.body;if(i instanceof R){if(i.bodyUsed)throw new TypeError("Already read");this.url=i.url,this.credentials=i.credentials,a.headers||(this.headers=new p(i.headers)),this.method=i.method,this.mode=i.mode,this.signal=i.signal,!d&&i._bodyInit!=null&&(d=i._bodyInit,i.bodyUsed=!0)}else this.url=String(i);if(this.credentials=a.credentials||this.credentials||"same-origin",(a.headers||!this.headers)&&(this.headers=new p(a.headers)),this.method=j(a.method||this.method||"GET"),this.mode=a.mode||this.mode||null,this.signal=a.signal||this.signal||(function(){if("AbortController"in r){var f=new AbortController;return f.signal}})(),this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&d)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(d),(this.method==="GET"||this.method==="HEAD")&&(a.cache==="no-store"||a.cache==="no-cache")){var g=/([?&])_=[^&]*/;if(g.test(this.url))this.url=this.url.replace(g,"$1_="+new Date().getTime());else{var y=/\?/;this.url+=(y.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}R.prototype.clone=function(){return new R(this,{body:this._bodyInit})};function ee(i){var a=new FormData;return i.trim().split("&").forEach(function(d){if(d){var g=d.split("="),y=g.shift().replace(/\+/g," "),f=g.join("=").replace(/\+/g," ");a.append(decodeURIComponent(y),decodeURIComponent(f))}}),a}function te(i){var a=new p,d=i.replace(/\r?\n[\t ]+/g," ");return d.split("\r").map(function(g){return g.indexOf(`
2
+ `)===0?g.substr(1,g.length):g}).forEach(function(g){var y=g.split(":"),f=y.shift().trim();if(f){var D=y.join(":").trim();try{a.append(f,D)}catch(A){console.warn("Response "+A.message)}}}),a}J.call(R.prototype);function k(i,a){if(!(this instanceof k))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(a||(a={}),this.type="default",this.status=a.status===void 0?200:a.status,this.status<200||this.status>599)throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].");this.ok=this.status>=200&&this.status<300,this.statusText=a.statusText===void 0?"":""+a.statusText,this.headers=new p(a.headers),this.url=a.url||"",this._initBody(i)}J.call(k.prototype),k.prototype.clone=function(){return new k(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new p(this.headers),url:this.url})},k.error=function(){var i=new k(null,{status:200,statusText:""});return i.ok=!1,i.status=0,i.type="error",i};var ne=[301,302,303,307,308];k.redirect=function(i,a){if(ne.indexOf(a)===-1)throw new RangeError("Invalid status code");return new k(null,{status:a,headers:{location:i}})},t.DOMException=r.DOMException;try{new t.DOMException}catch{t.DOMException=function(a,d){this.message=a,this.name=d;var g=Error(a);this.stack=g.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function I(i,a){return new Promise(function(d,g){var y=new R(i,a);if(y.signal&&y.signal.aborted)return g(new t.DOMException("Aborted","AbortError"));var f=new XMLHttpRequest;function D(){f.abort()}f.onload=function(){var m={statusText:f.statusText,headers:te(f.getAllResponseHeaders()||"")};y.url.indexOf("file://")===0&&(f.status<200||f.status>599)?m.status=200:m.status=f.status,m.url="responseURL"in f?f.responseURL:m.headers.get("X-Request-URL");var S="response"in f?f.response:f.responseText;setTimeout(function(){d(new k(S,m))},0)},f.onerror=function(){setTimeout(function(){g(new TypeError("Network request failed"))},0)},f.ontimeout=function(){setTimeout(function(){g(new TypeError("Network request timed out"))},0)},f.onabort=function(){setTimeout(function(){g(new t.DOMException("Aborted","AbortError"))},0)};function A(m){try{return m===""&&r.location.href?r.location.href:m}catch{return m}}if(f.open(y.method,A(y.url),!0),y.credentials==="include"?f.withCredentials=!0:y.credentials==="omit"&&(f.withCredentials=!1),"responseType"in f&&(n.blob?f.responseType="blob":n.arrayBuffer&&(f.responseType="arraybuffer")),a&&typeof a.headers=="object"&&!(a.headers instanceof p||r.Headers&&a.headers instanceof r.Headers)){var q=[];Object.getOwnPropertyNames(a.headers).forEach(function(m){q.push(u(m)),f.setRequestHeader(m,h(a.headers[m]))}),y.headers.forEach(function(m,S){q.indexOf(S)===-1&&f.setRequestHeader(S,m)})}else y.headers.forEach(function(m,S){f.setRequestHeader(S,m)});y.signal&&(y.signal.addEventListener("abort",D),f.onreadystatechange=function(){f.readyState===4&&y.signal.removeEventListener("abort",D)}),f.send(typeof y._bodyInit>"u"?null:y._bodyInit)})}return I.polyfill=!0,r.fetch||(r.fetch=I,r.Headers=p,r.Request=R,r.Response=k),t.Headers=p,t.Request=R,t.Response=k,t.fetch=I,t})({})})(typeof self<"u"?self:z)});var b=[];for(let s=0;s<256;++s)b.push((s+256).toString(16).slice(1));function H(s,e=0){return(b[s[e+0]]+b[s[e+1]]+b[s[e+2]]+b[s[e+3]]+"-"+b[s[e+4]]+b[s[e+5]]+"-"+b[s[e+6]]+b[s[e+7]]+"-"+b[s[e+8]]+b[s[e+9]]+"-"+b[s[e+10]]+b[s[e+11]]+b[s[e+12]]+b[s[e+13]]+b[s[e+14]]+b[s[e+15]]).toLowerCase()}var P,de=new Uint8Array(16);function N(){if(!P){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");P=crypto.getRandomValues.bind(crypto)}return P(de)}var fe=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),B={randomUUID:fe};function he(s,e,t){s=s||{};let r=s.random??s.rng?.()??N();if(r.length<16)throw new Error("Random bytes length must be >= 16");if(r[6]=r[6]&15|64,r[8]=r[8]&63|128,e){if(t=t||0,t<0||t+16>e.length)throw new RangeError(`UUID byte range ${t}:${t+15} is out of buffer bounds`);for(let n=0;n<16;++n)e[t+n]=r[n];return e}return H(r)}function ge(s,e,t){return B.randomUUID&&!e&&!s?B.randomUUID():he(s,e,t)}var C=ge;var it=ue(G());function F(s){return pe(s,!1)}function pe(s,e){return s==null?s:{companyId:s.company_id==null?void 0:s.company_id,error:s.error==null?void 0:s.error,featureAllocation:s.feature_allocation==null?void 0:s.feature_allocation,featureUsage:s.feature_usage==null?void 0:s.feature_usage,featureUsageEvent:s.feature_usage_event==null?void 0:s.feature_usage_event,featureUsagePeriod:s.feature_usage_period==null?void 0:s.feature_usage_period,featureUsageResetAt:s.feature_usage_reset_at==null?void 0:new Date(s.feature_usage_reset_at),flag:s.flag,flagId:s.flag_id==null?void 0:s.flag_id,reason:s.reason,ruleId:s.rule_id==null?void 0:s.rule_id,ruleType:s.rule_type==null?void 0:s.rule_type,userId:s.user_id==null?void 0:s.user_id,value:s.value}}function L(s){return ye(s,!1)}function ye(s,e=!1){return s==null?s:{company_id:s.companyId,error:s.error,flag_id:s.flagId,flag_key:s.flagKey,reason:s.reason,req_company:s.reqCompany,req_user:s.reqUser,rule_id:s.ruleId,user_id:s.userId,value:s.value}}function _(s){return be(s,!1)}function be(s,e){return s==null?s:{data:F(s.data),params:s.params}}function X(s){return ve(s,!1)}function ve(s,e){return s==null?s:{flags:s.flags.map(F)}}function M(s){return ke(s,!1)}function ke(s,e){return s==null?s:{data:X(s.data),params:s.params}}var O=s=>{let{companyId:e,error:t,featureAllocation:r,featureUsage:n,featureUsageEvent:c,featureUsagePeriod:o,featureUsageResetAt:l,flag:u,flagId:h,reason:v,ruleId:p,ruleType:E,userId:x,value:T}=F(s);return{featureUsageExceeded:!T&&(E=="company_override_usage_exceeded"||E=="plan_entitlement_usage_exceeded"),companyId:e??void 0,error:t??void 0,featureAllocation:r??void 0,featureUsage:n??void 0,featureUsageEvent:c===null?void 0:c,featureUsagePeriod:o??void 0,featureUsageResetAt:l??void 0,flag:u,flagId:h??void 0,reason:v,ruleId:p??void 0,ruleType:E??void 0,userId:x??void 0,value:T}};function w(s){let e=Object.keys(s).reduce((t,r)=>{let c=Object.keys(s[r]||{}).sort().reduce((o,l)=>(o[l]=s[r][l],o),{});return t[r]=c,t},{});return JSON.stringify(e)}var $="1.2.14";var K="schematicId";var U=class{additionalHeaders={};apiKey;apiUrl="https://api.schematichq.com";conn=null;context={};debugEnabled=!1;offlineEnabled=!1;eventQueue;contextDependentEventQueue;eventUrl="https://c.schematichq.com";flagCheckListeners={};flagValueListeners={};isPending=!0;isPendingListeners=new Set;storage;useWebSocket=!1;checks={};featureUsageEventMap={};webSocketUrl="wss://api.schematichq.com";webSocketConnectionTimeout=1e4;webSocketReconnect=!0;webSocketMaxReconnectAttempts=7;webSocketMaxConnectionAttempts=3;webSocketInitialRetryDelay=1e3;webSocketMaxRetryDelay=3e4;wsReconnectAttempts=0;wsReconnectTimer=null;wsIntentionalDisconnect=!1;currentWebSocket=null;isConnecting=!1;maxEventQueueSize=100;maxEventRetries=5;eventRetryInitialDelay=1e3;eventRetryMaxDelay=3e4;retryTimer=null;flagValueDefaults={};flagCheckDefaults={};constructor(e,t){if(this.apiKey=e,this.eventQueue=[],this.contextDependentEventQueue=[],this.useWebSocket=t?.useWebSocket??!1,this.debugEnabled=t?.debug??!1,this.offlineEnabled=t?.offline??!1,typeof window<"u"&&typeof window.location<"u"){let r=new URLSearchParams(window.location.search),n=r.get("schematic_debug");n!==null&&(n===""||n==="true"||n==="1")&&(this.debugEnabled=!0);let c=r.get("schematic_offline");c!==null&&(c===""||c==="true"||c==="1")&&(this.offlineEnabled=!0,this.debugEnabled=!0)}this.offlineEnabled&&t?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${$}`,...t?.additionalHeaders??{}},t?.storage?this.storage=t.storage:typeof localStorage<"u"&&(this.storage=localStorage),t?.apiUrl!==void 0&&(this.apiUrl=t.apiUrl),t?.eventUrl!==void 0&&(this.eventUrl=t.eventUrl),t?.webSocketUrl!==void 0&&(this.webSocketUrl=t.webSocketUrl),t?.webSocketConnectionTimeout!==void 0&&(this.webSocketConnectionTimeout=t.webSocketConnectionTimeout),t?.webSocketReconnect!==void 0&&(this.webSocketReconnect=t.webSocketReconnect),t?.webSocketMaxReconnectAttempts!==void 0&&(this.webSocketMaxReconnectAttempts=t.webSocketMaxReconnectAttempts),t?.webSocketInitialRetryDelay!==void 0&&(this.webSocketInitialRetryDelay=t.webSocketInitialRetryDelay),t?.webSocketMaxRetryDelay!==void 0&&(this.webSocketMaxRetryDelay=t.webSocketMaxRetryDelay),t?.maxEventQueueSize!==void 0&&(this.maxEventQueueSize=t.maxEventQueueSize),t?.maxEventRetries!==void 0&&(this.maxEventRetries=t.maxEventRetries),t?.eventRetryInitialDelay!==void 0&&(this.eventRetryInitialDelay=t.eventRetryInitialDelay),t?.eventRetryMaxDelay!==void 0&&(this.eventRetryMaxDelay=t.eventRetryMaxDelay),t?.flagValueDefaults!==void 0&&(this.flagValueDefaults=t.flagValueDefaults),t?.flagCheckDefaults!==void 0&&(this.flagCheckDefaults=t.flagCheckDefaults),typeof window<"u"&&window?.addEventListener&&(window.addEventListener("beforeunload",()=>{this.flushEventQueue(),this.flushContextDependentEventQueue()}),this.useWebSocket&&(window.addEventListener("offline",()=>{this.debug("Browser went offline, closing WebSocket connection"),this.handleNetworkOffline()}),window.addEventListener("online",()=>{this.debug("Browser came online, attempting to reconnect WebSocket"),this.handleNetworkOnline()}))),this.offlineEnabled?this.debug("Initialized with offline mode enabled - no network requests will be made"):this.debugEnabled&&this.debug("Initialized with debug mode enabled")}resolveFallbackValue(e,t){return t!==void 0?t:e in this.flagCheckDefaults?this.flagCheckDefaults[e].value:e in this.flagValueDefaults?this.flagValueDefaults[e]:!1}resolveFallbackCheckFlagReturn(e,t,r="Fallback value used",n){if(t!==void 0)return{flag:e,value:t,reason:r,error:n};if(e in this.flagCheckDefaults){let c=this.flagCheckDefaults[e];return{...c,flag:e,reason:n!==void 0?r:c.reason,error:n}}return e in this.flagValueDefaults?{flag:e,value:this.flagValueDefaults[e],reason:r,error:n}:{flag:e,value:!1,reason:r,error:n}}async checkFlag(e){let{fallback:t,key:r}=e,n=e.context||this.context,c=w(n);if(this.debug(`checkFlag: ${r}`,{context:n,fallback:t}),this.isOffline()){let o=this.resolveFallbackCheckFlagReturn(r,t,"Offline mode - using initialization defaults");return this.debug(`checkFlag offline result: ${r}`,{value:o.value,offlineMode:!0}),o.value}if(!this.useWebSocket){let o=`${this.apiUrl}/flags/${r}/check`;return fetch(o,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(n)}).then(l=>{if(!l.ok)throw new Error("Network response was not ok");return l.json()}).then(l=>{let u=_(l);this.debug(`checkFlag result: ${r}`,u);let h=O(u.data);return typeof h.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(h),this.submitFlagCheckEvent(r,h,n),h.value}).catch(l=>{console.warn("There was a problem with the fetch operation:",l);let u=this.resolveFallbackCheckFlagReturn(r,t,"API request failed",l instanceof Error?l.message:String(l));return this.submitFlagCheckEvent(r,u,n),u.value})}try{let o=this.checks[c];if(this.conn!==null&&typeof o<"u"&&typeof o[r]<"u")return this.debug(`checkFlag cached result: ${r}`,o[r]),o[r].value;if(this.isOffline())return this.resolveFallbackValue(r,t);try{await this.setContext(n)}catch(v){return console.warn("WebSocket connection failed, falling back to REST:",v),this.fallbackToRest(r,n,t)}let u=(this.checks[c]??{})[r],h=u?.value??this.resolveFallbackValue(r,t);return this.debug(`checkFlag WebSocket result: ${r}`,typeof u<"u"?u:{value:h,fallbackUsed:!0}),typeof u<"u"&&this.submitFlagCheckEvent(r,u,n),h}catch(o){console.error("Unexpected error in checkFlag:",o);let l=this.resolveFallbackCheckFlagReturn(r,t,"Unexpected error in flag check",o instanceof Error?o.message:String(o));return this.submitFlagCheckEvent(r,l,n),l.value}}debug(e,...t){this.debugEnabled&&console.log(`[Schematic] ${e}`,...t)}createPersistentMessageHandler(e){return t=>{let r=JSON.parse(t.data);this.debug("WebSocket persistent message received:",r),w(e)in this.checks||(this.checks[w(e)]={}),(r.flags??[]).forEach(n=>{let c=O(n),o=w(e);this.checks[o]===void 0&&(this.checks[o]={}),this.checks[o][c.flag]=c,this.debug("WebSocket flag update:",{flag:c.flag,value:c.value,flagCheck:c}),typeof c.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(c),((this.flagCheckListeners[n.flag]?.size??0)>0||(this.flagValueListeners[n.flag]?.size??0)>0)&&this.submitFlagCheckEvent(c.flag,c,e),this.debug(`About to notify listeners for flag ${n.flag}`,{flag:n.flag,value:c.value}),this.notifyFlagCheckListeners(n.flag,c),this.notifyFlagValueListeners(n.flag,c.value),this.debug(`Finished notifying listeners for flag ${n.flag}`,{flag:n.flag,value:c.value})}),this.flushContextDependentEventQueue(),this.setIsPending(!1)}}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(e,t,r){let n={flagKey:e,value:t.value,reason:t.reason,flagId:t.flagId,ruleId:t.ruleId,companyId:t.companyId,userId:t.userId,error:t.error,reqCompany:r.company,reqUser:r.user};return this.debug("submitting flag check event:",n),this.handleEvent("flag_check",L(n))}async fallbackToRest(e,t,r){if(this.isOffline()){let n=this.resolveFallbackValue(e,r);return this.debug(`fallbackToRest offline result: ${e}`,{value:n,offlineMode:!0}),n}try{let n=`${this.apiUrl}/flags/${e}/check`,c=await fetch(n,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(t)});if(!c.ok)throw new Error("Network response was not ok");let o=await c.json(),l=_(o);this.debug(`fallbackToRest result: ${e}`,l);let u=O(l.data);return typeof u.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(u),this.submitFlagCheckEvent(e,u,t),u.value}catch(n){console.warn("REST API call failed, using fallback value:",n);let c=this.resolveFallbackCheckFlagReturn(e,r,"API request failed (fallback)",n instanceof Error?n.message:String(n));return this.submitFlagCheckEvent(e,c,t),c.value}}checkFlags=async e=>{if(e=e||this.context,this.debug("checkFlags",{context:e}),this.isOffline())return this.debug("checkFlags offline result: returning empty object"),{};let t=`${this.apiUrl}/flags/check`,r=JSON.stringify(e);return fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:r}).then(n=>{if(!n.ok)throw new Error("Network response was not ok");return n.json()}).then(n=>{let c=M(n);return this.debug("checkFlags result:",c),(c?.data?.flags??[]).reduce((o,l)=>(o[l.flag]=l.value,o),{})}).catch(n=>(console.warn("There was a problem with the fetch operation:",n),{}))};identify=e=>{this.debug("identify:",e);try{this.setContext({company:e.company?.keys,user:e.keys})}catch(t){console.warn("Error setting context:",t)}return this.handleEvent("identify",e)};setContext=async e=>{if(this.isOffline()||!this.useWebSocket)return this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),Promise.resolve();try{if(this.setIsPending(!0),!this.conn){if(this.isConnecting){for(this.debug("Connection already in progress, waiting for it to complete");this.isConnecting&&this.conn===null;)await new Promise(r=>setTimeout(r,10));if(this.conn!==null){let r=await this.conn;await this.wsSendMessage(r,e);return}}this.wsReconnectTimer!==null&&(this.debug("Cancelling scheduled reconnection, connecting immediately"),clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.isConnecting=!0;try{this.conn=this.wsConnect();let r=await this.conn;this.isConnecting=!1,await this.wsSendMessage(r,e);return}catch(r){throw this.isConnecting=!1,r}}let t=await this.conn;await this.wsSendMessage(t,e)}catch(t){throw console.warn("Failed to establish WebSocket connection:",t),t}};track=e=>{let{company:t,user:r,event:n,traits:c,quantity:o=1}=e;if(!this.hasContext(t,r)){this.debug(`track: queuing event "${n}" until context is available`);let u={api_key:this.apiKey,body:{company:t,event:n,traits:c??{},user:r,quantity:o},sent_at:new Date().toISOString(),tracker_event_id:C(),tracker_user_id:this.getAnonymousId(),type:"track"};return this.contextDependentEventQueue.push(u),Promise.resolve()}let l={company:t??this.context.company,event:n,traits:c??{},user:r??this.context.user,quantity:o};return this.debug("track:",l),n in this.featureUsageEventMap&&this.optimisticallyUpdateFeatureUsage(n,o),this.handleEvent("track",l)};optimisticallyUpdateFeatureUsage=(e,t=1)=>{let r=this.featureUsageEventMap[e];r!=null&&(this.debug(`Optimistically updating feature usage for event: ${e}`,{quantity:t}),Object.entries(r).forEach(([n,c])=>{if(c===void 0)return;let o={...c};if(typeof o.featureUsage=="number"){if(o.featureUsage+=t,typeof o.featureAllocation=="number"){let u=o.featureUsageExceeded===!0,h=o.featureUsage>=o.featureAllocation;h!==u&&(o.featureUsageExceeded=h,h&&(o.value=!1),this.debug(`Usage limit status changed for flag: ${n}`,{was:u?"exceeded":"within limits",now:h?"exceeded":"within limits",featureUsage:o.featureUsage,featureAllocation:o.featureAllocation,value:o.value}))}this.featureUsageEventMap[e]!==void 0&&(this.featureUsageEventMap[e][n]=o);let l=w(this.context);this.checks[l]!==void 0&&this.checks[l]!==null&&(this.checks[l][n]=o),this.notifyFlagCheckListeners(n,o),this.notifyFlagValueListeners(n,o.value)}}))};hasContext=(e,t)=>{let r=e!=null&&Object.keys(e).length>0||t!=null&&Object.keys(t).length>0,n=this.context.company!==void 0&&this.context.company!==null&&Object.keys(this.context.company).length>0||this.context.user!==void 0&&this.context.user!==null&&Object.keys(this.context.user).length>0;return r||n};flushContextDependentEventQueue=()=>{for(this.debug(`flushing ${this.contextDependentEventQueue.length} context-dependent events`);this.contextDependentEventQueue.length>0;){let e=this.contextDependentEventQueue.shift();if(e)if(e.type==="track"&&typeof e.body=="object"&&e.body!==null){let t=e.body,r={...t,company:t.company??this.context.company,user:t.user??this.context.user},n={...e,body:r,sent_at:new Date().toISOString()};this.sendEvent(n)}else this.sendEvent(e)}};startRetryTimer=()=>{this.retryTimer===null&&(this.retryTimer=setInterval(()=>{this.flushEventQueue().catch(e=>{this.debug("Error in retry timer flush:",e)}),this.eventQueue.length===0&&this.stopRetryTimer()},5e3),this.debug("Started retry timer"))};stopRetryTimer=()=>{this.retryTimer!==null&&(clearInterval(this.retryTimer),this.retryTimer=null,this.debug("Stopped retry timer"))};flushEventQueue=async()=>{if(this.eventQueue.length===0)return;let e=Date.now(),t=[],r=[];for(let n of this.eventQueue)n.next_retry_at===void 0||n.next_retry_at<=e?t.push(n):r.push(n);if(t.length===0){this.debug(`No events ready for retry yet (${r.length} still in backoff)`);return}this.debug(`Flushing event queue: ${t.length} ready, ${r.length} waiting`),this.eventQueue=r;for(let n of t)try{await this.sendEvent(n),this.debug("Queued event sent successfully:",n.type)}catch(c){this.debug("Failed to send queued event:",c)}};getAnonymousId=()=>{if(!this.storage)return C();let e=this.storage.getItem(K);if(typeof e<"u")return e;let t=C();return this.storage.setItem(K,t),t};handleEvent=(e,t)=>{let r={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:C(),tracker_user_id:this.getAnonymousId(),type:e};return typeof document<"u"&&document?.hidden?this.storeEvent(r):this.sendEvent(r)};sendEvent=async e=>{let t=`${this.eventUrl}/e`,r=JSON.stringify(e);if(this.debug("sending event:",{url:t,event:e}),this.isOffline())return this.debug("event not sent (offline mode):",{event:e}),Promise.resolve();try{let n=await fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:r});if(!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText}`);this.debug("event sent:",{status:n.status,statusText:n.statusText})}catch(n){let c=(e.retry_count??0)+1;if(c<=this.maxEventRetries){this.debug(`Event failed to send (attempt ${c}/${this.maxEventRetries}), queueing for retry:`,n);let o=this.eventRetryInitialDelay*Math.pow(2,c-1),l=Math.min(o,this.eventRetryMaxDelay),u=Date.now()+l,h={...e,retry_count:c,next_retry_at:u};this.eventQueue.length<this.maxEventQueueSize?(this.eventQueue.push(h),this.debug(`Event queued for retry in ${l}ms (${this.eventQueue.length}/${this.maxEventQueueSize})`)):(this.debug(`Event queue full (${this.maxEventQueueSize}), dropping oldest event`),this.eventQueue.shift(),this.eventQueue.push(h)),this.startRetryTimer()}else this.debug(`Event failed permanently after ${this.maxEventRetries} attempts, dropping:`,n)}return Promise.resolve()};storeEvent=e=>(this.eventQueue.push(e),Promise.resolve());forceReconnect=async()=>this.reconnect({force:!0});reconnectIfNeeded=async()=>this.reconnect({force:!1});reconnect=async e=>{let{force:t}=e,r=t?"forceReconnect":"reconnectIfNeeded";if(this.isOffline())return this.debug(`${r}: skipped (offline mode)`),Promise.resolve();if(!t&&this.conn!==null)try{if((await this.conn).readyState===WebSocket.OPEN)return this.debug(`${r}: connection is healthy, skipping`),Promise.resolve()}catch{}if(this.debug(`${r}: ${t?"forcing immediate reconnection":"reconnecting"}`),this.wsIntentionalDisconnect=!1,this.wsReconnectTimer!==null&&(this.debug(`${r}: cancelling pending reconnection timer`),clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.wsReconnectAttempts=0,this.conn!==null){this.debug(`${r}: closing existing connection`);try{let n=await this.conn;this.currentWebSocket===n&&(this.currentWebSocket=null),(n.readyState===WebSocket.OPEN||n.readyState===WebSocket.CONNECTING)&&n.close()}catch(n){this.debug(`${r}: error closing existing connection:`,n)}this.conn=null,this.isConnecting=!1}if(this.context.company!==void 0||this.context.user!==void 0){this.debug(`${r}: reconnecting with existing context`);try{this.isConnecting=!0,this.conn=this.wsConnect();let n=await this.conn;this.isConnecting=!1,await this.wsSendMessage(n,this.context,!0),this.debug(`${r}: reconnection successful`)}catch(n){this.isConnecting=!1,this.debug(`${r}: reconnection failed:`,n)}}else this.debug(`${r}: no context set, skipping reconnection`);return Promise.resolve()};cleanup=async()=>{if(this.isOffline())return this.debug("cleanup: skipped (offline mode)"),Promise.resolve();if(this.wsIntentionalDisconnect=!0,this.wsReconnectTimer!==null&&(clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.stopRetryTimer(),this.conn)try{let e=await this.conn;this.currentWebSocket===e&&(this.debug("Cleaning up current websocket tracking"),this.currentWebSocket=null),e.close()}catch(e){console.warn("Error during cleanup:",e)}finally{this.conn=null,this.currentWebSocket=null,this.isConnecting=!1}};calculateReconnectDelay=()=>{let e=this.webSocketInitialRetryDelay*Math.pow(2,this.wsReconnectAttempts),t=Math.min(e,this.webSocketMaxRetryDelay),r=Math.random()*t*.5,n=t+r;return this.debug(`Reconnect delay calculated: ${n.toFixed(0)}ms (attempt ${this.wsReconnectAttempts+1}/${this.webSocketMaxReconnectAttempts})`),n};handleNetworkOffline=async()=>{if(this.conn!==null){try{let e=await this.conn;(e.readyState===WebSocket.OPEN||e.readyState===WebSocket.CONNECTING)&&e.close()}catch(e){this.debug("Error closing connection on offline:",e)}this.conn=null}this.wsReconnectTimer!==null&&(clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null)};handleNetworkOnline=()=>{this.debug("Network online, attempting reconnection and flushing queued events"),this.wsReconnectAttempts=0,this.wsReconnectTimer!==null&&(clearTimeout(this.wsReconnectTimer),this.wsReconnectTimer=null),this.flushEventQueue().catch(e=>{this.debug("Error flushing event queue on network online:",e)}),this.attemptReconnect()};attemptReconnect=()=>{if(this.wsReconnectAttempts>=this.webSocketMaxReconnectAttempts){this.debug(`Maximum reconnection attempts (${this.webSocketMaxReconnectAttempts}) reached, giving up`);return}if(this.wsReconnectTimer!==null){this.debug("Reconnection attempt already scheduled, ignoring duplicate request");return}let e=this.calculateReconnectDelay();this.debug(`Scheduling reconnection attempt ${this.wsReconnectAttempts+1}/${this.webSocketMaxReconnectAttempts} in ${e.toFixed(0)}ms`),this.wsReconnectTimer=setTimeout(async()=>{this.wsReconnectTimer=null,this.wsReconnectAttempts++,this.debug(`Attempting to reconnect (attempt ${this.wsReconnectAttempts}/${this.webSocketMaxReconnectAttempts})`);try{if(this.conn!==null){this.debug("Cleaning up existing connection before reconnection");try{let t=await this.conn;this.currentWebSocket===t&&(this.debug("Existing websocket is current, will be replaced"),this.currentWebSocket=null),(t.readyState===WebSocket.OPEN||t.readyState===WebSocket.CONNECTING)&&t.close()}catch(t){this.debug("Error cleaning up existing connection:",t)}this.conn=null,this.currentWebSocket=null,this.isConnecting=!1}this.isConnecting=!0;try{this.conn=this.wsConnect();let t=await this.conn;this.isConnecting=!1,this.debug("Reconnection context check:",{hasCompany:this.context.company!==void 0,hasUser:this.context.user!==void 0,context:this.context}),this.context.company!==void 0||this.context.user!==void 0?(this.debug("Reconnected, force re-sending context"),await this.wsSendMessage(t,this.context,!0)):(this.debug("No context to re-send after reconnection - websocket ready for new context"),this.debug("Setting up tracking for reconnected websocket (no context to send)"),this.currentWebSocket=t),this.flushEventQueue().catch(r=>{this.debug("Error flushing event queue after websocket reconnection:",r)}),this.debug("Reconnection successful")}catch(t){throw this.isConnecting=!1,t}}catch(t){this.debug("Reconnection attempt failed:",t)}},e)};wsConnect=async()=>{if(this.isOffline())throw this.debug("wsConnect: skipped (offline mode)"),new Error("WebSocket connection skipped in offline mode");let e=null,t=this.webSocketMaxConnectionAttempts;for(let r=0;r<t;r++)try{let n=await this.wsConnectOnce();return this.wsReconnectAttempts=0,n}catch(n){if(e=n instanceof Error?n:new Error(String(n)),!(e.message==="WebSocket connection timeout"))throw this.debug("WebSocket connection failed with non-timeout error, not retrying:",e.message),e;if(r<t-1){let o=this.webSocketInitialRetryDelay*Math.pow(2,r),l=Math.min(o,this.webSocketMaxRetryDelay),u=l*.2*Math.random(),h=l+u;this.debug(`WebSocket connection timeout (attempt ${r+1}/${t}), retrying in ${h.toFixed(0)}ms`),await new Promise(v=>setTimeout(v,h))}else this.debug(`WebSocket connection timeout (attempt ${r+1}/${t}), no more retries`)}throw e??new Error("WebSocket connection failed")};wsConnectOnce=()=>new Promise((e,t)=>{let r=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",r);let n=new WebSocket(r),c=Math.random().toString(36).substring(7);this.debug(`Creating WebSocket connection ${c} to ${r}`);let o=null,l=!1;o=setTimeout(()=>{l||(l=!0,this.debug(`WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`),n.close(),t(new Error("WebSocket connection timeout")))},this.webSocketConnectionTimeout),n.onopen=()=>{l||(l=!0,o!==null&&clearTimeout(o),this.wsIntentionalDisconnect=!1,this.debug(`WebSocket connection ${c} opened successfully`),e(n))},n.onerror=u=>{l||(l=!0,o!==null&&clearTimeout(o),this.debug(`WebSocket connection ${c} error:`,u),t(u))},n.onclose=()=>{o!==null&&clearTimeout(o),this.debug(`WebSocket connection ${c} closed`),this.conn=null,this.currentWebSocket===n&&(this.currentWebSocket=null,this.isConnecting=!1),!l&&!this.wsIntentionalDisconnect&&this.webSocketReconnect&&this.attemptReconnect()}});wsSendMessage=(e,t,r=!1)=>this.isOffline()?(this.debug("wsSendMessage: skipped (offline mode)"),this.setIsPending(!1),Promise.resolve()):new Promise((n,c)=>{if(!r&&w(t)==w(this.context))return this.debug("WebSocket context unchanged, skipping update"),n(this.setIsPending(!1));this.debug(r?"WebSocket force sending context (reconnection):":"WebSocket context updated:",t),this.context=t;let o=()=>{let l=!1,u=this.createPersistentMessageHandler(t),h=E=>{u(E),l||(l=!0,n())};e.addEventListener("message",h),this.currentWebSocket=e;let v=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${$}`,p={apiKey:this.apiKey,clientVersion:v,data:t};this.debug("WebSocket sending message:",p),e.send(JSON.stringify(p))};e.readyState===WebSocket.OPEN?(this.debug("WebSocket already open, sending message"),o()):e.readyState===WebSocket.CONNECTING?(this.debug("WebSocket connecting, waiting for open to send message"),e.addEventListener("open",o)):(this.debug("WebSocket is closed, cannot send message"),c("WebSocket is not open or connecting"))});getIsPending=()=>this.isPending;addIsPendingListener=e=>(this.isPendingListeners.add(e),()=>{this.isPendingListeners.delete(e)});setIsPending=e=>{this.isPending=e,this.isPendingListeners.forEach(t=>Ee(t,e))};getFlagCheck=e=>{let t=w(this.context),n=(this.checks[t]??{})[e];if(n!==void 0)return n;if(e in this.flagCheckDefaults||e in this.flagValueDefaults)return this.resolveFallbackCheckFlagReturn(e,void 0,"Default value used")};getFlagValue=e=>{let t=w(this.context),n=(this.checks[t]??{})[e];if(n?.value!==void 0)return n.value;if(e in this.flagCheckDefaults||e in this.flagValueDefaults)return this.resolveFallbackValue(e)};addFlagValueListener=(e,t)=>(e in this.flagValueListeners||(this.flagValueListeners[e]=new Set),this.flagValueListeners[e].add(t),()=>{this.flagValueListeners[e].delete(t)});addFlagCheckListener=(e,t)=>(e in this.flagCheckListeners||(this.flagCheckListeners[e]=new Set),this.flagCheckListeners[e].add(t),()=>{this.flagCheckListeners[e].delete(t)});notifyFlagCheckListeners=(e,t)=>{let r=this.flagCheckListeners?.[e]??[];r.size>0&&this.debug(`Notifying ${r.size} flag check listeners for ${e}`,t),typeof t.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(t),r.forEach(n=>we(n,t))};updateFeatureUsageEventMap=e=>{if(typeof e.featureUsageEvent!="string")return;let t=e.featureUsageEvent;(this.featureUsageEventMap[t]===void 0||this.featureUsageEventMap[t]===null)&&(this.featureUsageEventMap[t]={}),this.featureUsageEventMap[t]!==void 0&&(this.featureUsageEventMap[t][e.flag]=e),this.debug(`Updated featureUsageEventMap for event: ${t}, flag: ${e.flag}`,e)};notifyFlagValueListeners=(e,t)=>{let r=this.flagValueListeners?.[e]??[];r.size>0&&this.debug(`Notifying ${r.size} flag value listeners for ${e}`,{value:t}),r.forEach((n,c)=>{this.debug(`Calling listener ${c} for flag ${e}`,{flagKey:e,value:t}),Re(n,t),this.debug(`Listener ${c} for flag ${e} completed`,{flagKey:e,value:t})})}},Ee=(s,e)=>{s.length>0?s(e):s()},we=(s,e)=>{s.length>0?s(e):s()},Re=(s,e)=>{s.length>0?s(e):s()};window.Schematic=U;})();
3
3
  /* @preserve */
@@ -800,7 +800,7 @@ function contextString(context) {
800
800
  }
801
801
 
802
802
  // src/version.ts
803
- var version = "1.2.12";
803
+ var version = "1.2.14";
804
804
 
805
805
  // src/index.ts
806
806
  var anonymousIdKey = "schematicId";
@@ -827,6 +827,9 @@ var Schematic = class {
827
827
  webSocketConnectionTimeout = 1e4;
828
828
  webSocketReconnect = true;
829
829
  webSocketMaxReconnectAttempts = 7;
830
+ // Max attempts after connection disrupted
831
+ webSocketMaxConnectionAttempts = 3;
832
+ // Max attempts for initial connection
830
833
  webSocketInitialRetryDelay = 1e3;
831
834
  webSocketMaxRetryDelay = 3e4;
832
835
  wsReconnectAttempts = 0;
@@ -948,13 +951,17 @@ var Schematic = class {
948
951
  /**
949
952
  * Resolve fallback value according to priority order:
950
953
  * 1. Callsite fallback value (if provided)
951
- * 2. Initialization fallback value (flagValueDefaults)
952
- * 3. Default to false
954
+ * 2. Boolean value from flagCheckDefaults initialization option
955
+ * 3. Boolean value from flagValueDefaults initialization option
956
+ * 4. Default to false
953
957
  */
954
958
  resolveFallbackValue(key, callsiteFallback) {
955
959
  if (callsiteFallback !== void 0) {
956
960
  return callsiteFallback;
957
961
  }
962
+ if (key in this.flagCheckDefaults) {
963
+ return this.flagCheckDefaults[key].value;
964
+ }
958
965
  if (key in this.flagValueDefaults) {
959
966
  return this.flagValueDefaults[key];
960
967
  }
@@ -1050,7 +1057,7 @@ var Schematic = class {
1050
1057
  this.submitFlagCheckEvent(key, result, context);
1051
1058
  return result.value;
1052
1059
  }).catch((error) => {
1053
- console.error("There was a problem with the fetch operation:", error);
1060
+ console.warn("There was a problem with the fetch operation:", error);
1054
1061
  const errorResult = this.resolveFallbackCheckFlagReturn(
1055
1062
  key,
1056
1063
  fallback,
@@ -1073,7 +1080,7 @@ var Schematic = class {
1073
1080
  try {
1074
1081
  await this.setContext(context);
1075
1082
  } catch (error) {
1076
- console.error(
1083
+ console.warn(
1077
1084
  "WebSocket connection failed, falling back to REST:",
1078
1085
  error
1079
1086
  );
@@ -1216,7 +1223,7 @@ var Schematic = class {
1216
1223
  this.submitFlagCheckEvent(key, result, context);
1217
1224
  return result.value;
1218
1225
  } catch (error) {
1219
- console.error("REST API call failed, using fallback value:", error);
1226
+ console.warn("REST API call failed, using fallback value:", error);
1220
1227
  const errorResult = this.resolveFallbackCheckFlagReturn(
1221
1228
  key,
1222
1229
  fallback,
@@ -1265,7 +1272,7 @@ var Schematic = class {
1265
1272
  {}
1266
1273
  );
1267
1274
  }).catch((error) => {
1268
- console.error("There was a problem with the fetch operation:", error);
1275
+ console.warn("There was a problem with the fetch operation:", error);
1269
1276
  return {};
1270
1277
  });
1271
1278
  };
@@ -1282,7 +1289,7 @@ var Schematic = class {
1282
1289
  user: body.keys
1283
1290
  });
1284
1291
  } catch (error) {
1285
- console.error("Error setting context:", error);
1292
+ console.warn("Error setting context:", error);
1286
1293
  }
1287
1294
  return this.handleEvent("identify", body);
1288
1295
  };
@@ -1340,7 +1347,7 @@ var Schematic = class {
1340
1347
  const socket = await this.conn;
1341
1348
  await this.wsSendMessage(socket, context);
1342
1349
  } catch (error) {
1343
- console.error("Failed to establish WebSocket connection:", error);
1350
+ console.warn("Failed to establish WebSocket connection:", error);
1344
1351
  throw error;
1345
1352
  }
1346
1353
  };
@@ -1761,7 +1768,7 @@ var Schematic = class {
1761
1768
  }
1762
1769
  socket.close();
1763
1770
  } catch (error) {
1764
- console.error("Error during cleanup:", error);
1771
+ console.warn("Error during cleanup:", error);
1765
1772
  } finally {
1766
1773
  this.conn = null;
1767
1774
  this.currentWebSocket = null;
@@ -1904,14 +1911,49 @@ var Schematic = class {
1904
1911
  }
1905
1912
  }, delay);
1906
1913
  };
1907
- // Open a websocket connection
1908
- wsConnect = () => {
1914
+ // Open a websocket connection with retry logic for timeouts
1915
+ wsConnect = async () => {
1909
1916
  if (this.isOffline()) {
1910
1917
  this.debug("wsConnect: skipped (offline mode)");
1911
- return Promise.reject(
1912
- new Error("WebSocket connection skipped in offline mode")
1913
- );
1918
+ throw new Error("WebSocket connection skipped in offline mode");
1919
+ }
1920
+ let lastError = null;
1921
+ const maxAttempts = this.webSocketMaxConnectionAttempts;
1922
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1923
+ try {
1924
+ const socket = await this.wsConnectOnce();
1925
+ this.wsReconnectAttempts = 0;
1926
+ return socket;
1927
+ } catch (error) {
1928
+ lastError = error instanceof Error ? error : new Error(String(error));
1929
+ const isTimeout = lastError.message === "WebSocket connection timeout";
1930
+ if (!isTimeout) {
1931
+ this.debug(
1932
+ `WebSocket connection failed with non-timeout error, not retrying:`,
1933
+ lastError.message
1934
+ );
1935
+ throw lastError;
1936
+ }
1937
+ if (attempt < maxAttempts - 1) {
1938
+ const baseDelay = this.webSocketInitialRetryDelay * Math.pow(2, attempt);
1939
+ const cappedDelay = Math.min(baseDelay, this.webSocketMaxRetryDelay);
1940
+ const jitter = cappedDelay * 0.2 * Math.random();
1941
+ const delay = cappedDelay + jitter;
1942
+ this.debug(
1943
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), retrying in ${delay.toFixed(0)}ms`
1944
+ );
1945
+ await new Promise((resolve) => setTimeout(resolve, delay));
1946
+ } else {
1947
+ this.debug(
1948
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), no more retries`
1949
+ );
1950
+ }
1951
+ }
1914
1952
  }
1953
+ throw lastError ?? new Error("WebSocket connection failed");
1954
+ };
1955
+ // Single attempt to open a websocket connection (no retry logic)
1956
+ wsConnectOnce = () => {
1915
1957
  return new Promise((resolve, reject) => {
1916
1958
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1917
1959
  this.debug(`connecting to WebSocket:`, wsUrl);
@@ -1922,6 +1964,7 @@ var Schematic = class {
1922
1964
  let isResolved = false;
1923
1965
  timeoutId = setTimeout(() => {
1924
1966
  if (!isResolved) {
1967
+ isResolved = true;
1925
1968
  this.debug(
1926
1969
  `WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`
1927
1970
  );
@@ -1930,16 +1973,17 @@ var Schematic = class {
1930
1973
  }
1931
1974
  }, this.webSocketConnectionTimeout);
1932
1975
  webSocket.onopen = () => {
1976
+ if (isResolved) return;
1933
1977
  isResolved = true;
1934
1978
  if (timeoutId !== null) {
1935
1979
  clearTimeout(timeoutId);
1936
1980
  }
1937
- this.wsReconnectAttempts = 0;
1938
1981
  this.wsIntentionalDisconnect = false;
1939
1982
  this.debug(`WebSocket connection ${connectionId} opened successfully`);
1940
1983
  resolve(webSocket);
1941
1984
  };
1942
1985
  webSocket.onerror = (error) => {
1986
+ if (isResolved) return;
1943
1987
  isResolved = true;
1944
1988
  if (timeoutId !== null) {
1945
1989
  clearTimeout(timeoutId);
@@ -1948,7 +1992,6 @@ var Schematic = class {
1948
1992
  reject(error);
1949
1993
  };
1950
1994
  webSocket.onclose = () => {
1951
- isResolved = true;
1952
1995
  if (timeoutId !== null) {
1953
1996
  clearTimeout(timeoutId);
1954
1997
  }
@@ -1958,7 +2001,7 @@ var Schematic = class {
1958
2001
  this.currentWebSocket = null;
1959
2002
  this.isConnecting = false;
1960
2003
  }
1961
- if (!this.wsIntentionalDisconnect && this.webSocketReconnect) {
2004
+ if (!isResolved && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
1962
2005
  this.attemptReconnect();
1963
2006
  }
1964
2007
  };
@@ -2038,12 +2081,31 @@ var Schematic = class {
2038
2081
  getFlagCheck = (flagKey) => {
2039
2082
  const contextStr = contextString(this.context);
2040
2083
  const checks = this.checks[contextStr] ?? {};
2041
- return checks[flagKey];
2084
+ const check = checks[flagKey];
2085
+ if (check !== void 0) {
2086
+ return check;
2087
+ }
2088
+ if (flagKey in this.flagCheckDefaults || flagKey in this.flagValueDefaults) {
2089
+ return this.resolveFallbackCheckFlagReturn(
2090
+ flagKey,
2091
+ void 0,
2092
+ "Default value used"
2093
+ );
2094
+ }
2095
+ return void 0;
2042
2096
  };
2043
2097
  // flagValues state
2044
2098
  getFlagValue = (flagKey) => {
2045
- const check = this.getFlagCheck(flagKey);
2046
- return check?.value;
2099
+ const contextStr = contextString(this.context);
2100
+ const checks = this.checks[contextStr] ?? {};
2101
+ const check = checks[flagKey];
2102
+ if (check?.value !== void 0) {
2103
+ return check.value;
2104
+ }
2105
+ if (flagKey in this.flagCheckDefaults || flagKey in this.flagValueDefaults) {
2106
+ return this.resolveFallbackValue(flagKey);
2107
+ }
2108
+ return void 0;
2047
2109
  };
2048
2110
  /** Register an event listener that will be notified with the boolean value for a given flag when this value changes */
2049
2111
  addFlagValueListener = (flagKey, listener) => {
@@ -373,6 +373,7 @@ export declare class Schematic {
373
373
  private webSocketConnectionTimeout;
374
374
  private webSocketReconnect;
375
375
  private webSocketMaxReconnectAttempts;
376
+ private webSocketMaxConnectionAttempts;
376
377
  private webSocketInitialRetryDelay;
377
378
  private webSocketMaxRetryDelay;
378
379
  private wsReconnectAttempts;
@@ -391,8 +392,9 @@ export declare class Schematic {
391
392
  /**
392
393
  * Resolve fallback value according to priority order:
393
394
  * 1. Callsite fallback value (if provided)
394
- * 2. Initialization fallback value (flagValueDefaults)
395
- * 3. Default to false
395
+ * 2. Boolean value from flagCheckDefaults initialization option
396
+ * 3. Boolean value from flagValueDefaults initialization option
397
+ * 4. Default to false
396
398
  */
397
399
  private resolveFallbackValue;
398
400
  /**
@@ -566,6 +568,7 @@ export declare class Schematic {
566
568
  */
567
569
  private attemptReconnect;
568
570
  private wsConnect;
571
+ private wsConnectOnce;
569
572
  private wsSendMessage;
570
573
  /**
571
574
  * State management
@@ -781,7 +781,7 @@ function contextString(context) {
781
781
  }
782
782
 
783
783
  // src/version.ts
784
- var version = "1.2.12";
784
+ var version = "1.2.14";
785
785
 
786
786
  // src/index.ts
787
787
  var anonymousIdKey = "schematicId";
@@ -808,6 +808,9 @@ var Schematic = class {
808
808
  webSocketConnectionTimeout = 1e4;
809
809
  webSocketReconnect = true;
810
810
  webSocketMaxReconnectAttempts = 7;
811
+ // Max attempts after connection disrupted
812
+ webSocketMaxConnectionAttempts = 3;
813
+ // Max attempts for initial connection
811
814
  webSocketInitialRetryDelay = 1e3;
812
815
  webSocketMaxRetryDelay = 3e4;
813
816
  wsReconnectAttempts = 0;
@@ -929,13 +932,17 @@ var Schematic = class {
929
932
  /**
930
933
  * Resolve fallback value according to priority order:
931
934
  * 1. Callsite fallback value (if provided)
932
- * 2. Initialization fallback value (flagValueDefaults)
933
- * 3. Default to false
935
+ * 2. Boolean value from flagCheckDefaults initialization option
936
+ * 3. Boolean value from flagValueDefaults initialization option
937
+ * 4. Default to false
934
938
  */
935
939
  resolveFallbackValue(key, callsiteFallback) {
936
940
  if (callsiteFallback !== void 0) {
937
941
  return callsiteFallback;
938
942
  }
943
+ if (key in this.flagCheckDefaults) {
944
+ return this.flagCheckDefaults[key].value;
945
+ }
939
946
  if (key in this.flagValueDefaults) {
940
947
  return this.flagValueDefaults[key];
941
948
  }
@@ -1031,7 +1038,7 @@ var Schematic = class {
1031
1038
  this.submitFlagCheckEvent(key, result, context);
1032
1039
  return result.value;
1033
1040
  }).catch((error) => {
1034
- console.error("There was a problem with the fetch operation:", error);
1041
+ console.warn("There was a problem with the fetch operation:", error);
1035
1042
  const errorResult = this.resolveFallbackCheckFlagReturn(
1036
1043
  key,
1037
1044
  fallback,
@@ -1054,7 +1061,7 @@ var Schematic = class {
1054
1061
  try {
1055
1062
  await this.setContext(context);
1056
1063
  } catch (error) {
1057
- console.error(
1064
+ console.warn(
1058
1065
  "WebSocket connection failed, falling back to REST:",
1059
1066
  error
1060
1067
  );
@@ -1197,7 +1204,7 @@ var Schematic = class {
1197
1204
  this.submitFlagCheckEvent(key, result, context);
1198
1205
  return result.value;
1199
1206
  } catch (error) {
1200
- console.error("REST API call failed, using fallback value:", error);
1207
+ console.warn("REST API call failed, using fallback value:", error);
1201
1208
  const errorResult = this.resolveFallbackCheckFlagReturn(
1202
1209
  key,
1203
1210
  fallback,
@@ -1246,7 +1253,7 @@ var Schematic = class {
1246
1253
  {}
1247
1254
  );
1248
1255
  }).catch((error) => {
1249
- console.error("There was a problem with the fetch operation:", error);
1256
+ console.warn("There was a problem with the fetch operation:", error);
1250
1257
  return {};
1251
1258
  });
1252
1259
  };
@@ -1263,7 +1270,7 @@ var Schematic = class {
1263
1270
  user: body.keys
1264
1271
  });
1265
1272
  } catch (error) {
1266
- console.error("Error setting context:", error);
1273
+ console.warn("Error setting context:", error);
1267
1274
  }
1268
1275
  return this.handleEvent("identify", body);
1269
1276
  };
@@ -1321,7 +1328,7 @@ var Schematic = class {
1321
1328
  const socket = await this.conn;
1322
1329
  await this.wsSendMessage(socket, context);
1323
1330
  } catch (error) {
1324
- console.error("Failed to establish WebSocket connection:", error);
1331
+ console.warn("Failed to establish WebSocket connection:", error);
1325
1332
  throw error;
1326
1333
  }
1327
1334
  };
@@ -1742,7 +1749,7 @@ var Schematic = class {
1742
1749
  }
1743
1750
  socket.close();
1744
1751
  } catch (error) {
1745
- console.error("Error during cleanup:", error);
1752
+ console.warn("Error during cleanup:", error);
1746
1753
  } finally {
1747
1754
  this.conn = null;
1748
1755
  this.currentWebSocket = null;
@@ -1885,14 +1892,49 @@ var Schematic = class {
1885
1892
  }
1886
1893
  }, delay);
1887
1894
  };
1888
- // Open a websocket connection
1889
- wsConnect = () => {
1895
+ // Open a websocket connection with retry logic for timeouts
1896
+ wsConnect = async () => {
1890
1897
  if (this.isOffline()) {
1891
1898
  this.debug("wsConnect: skipped (offline mode)");
1892
- return Promise.reject(
1893
- new Error("WebSocket connection skipped in offline mode")
1894
- );
1899
+ throw new Error("WebSocket connection skipped in offline mode");
1900
+ }
1901
+ let lastError = null;
1902
+ const maxAttempts = this.webSocketMaxConnectionAttempts;
1903
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1904
+ try {
1905
+ const socket = await this.wsConnectOnce();
1906
+ this.wsReconnectAttempts = 0;
1907
+ return socket;
1908
+ } catch (error) {
1909
+ lastError = error instanceof Error ? error : new Error(String(error));
1910
+ const isTimeout = lastError.message === "WebSocket connection timeout";
1911
+ if (!isTimeout) {
1912
+ this.debug(
1913
+ `WebSocket connection failed with non-timeout error, not retrying:`,
1914
+ lastError.message
1915
+ );
1916
+ throw lastError;
1917
+ }
1918
+ if (attempt < maxAttempts - 1) {
1919
+ const baseDelay = this.webSocketInitialRetryDelay * Math.pow(2, attempt);
1920
+ const cappedDelay = Math.min(baseDelay, this.webSocketMaxRetryDelay);
1921
+ const jitter = cappedDelay * 0.2 * Math.random();
1922
+ const delay = cappedDelay + jitter;
1923
+ this.debug(
1924
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), retrying in ${delay.toFixed(0)}ms`
1925
+ );
1926
+ await new Promise((resolve) => setTimeout(resolve, delay));
1927
+ } else {
1928
+ this.debug(
1929
+ `WebSocket connection timeout (attempt ${attempt + 1}/${maxAttempts}), no more retries`
1930
+ );
1931
+ }
1932
+ }
1895
1933
  }
1934
+ throw lastError ?? new Error("WebSocket connection failed");
1935
+ };
1936
+ // Single attempt to open a websocket connection (no retry logic)
1937
+ wsConnectOnce = () => {
1896
1938
  return new Promise((resolve, reject) => {
1897
1939
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1898
1940
  this.debug(`connecting to WebSocket:`, wsUrl);
@@ -1903,6 +1945,7 @@ var Schematic = class {
1903
1945
  let isResolved = false;
1904
1946
  timeoutId = setTimeout(() => {
1905
1947
  if (!isResolved) {
1948
+ isResolved = true;
1906
1949
  this.debug(
1907
1950
  `WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`
1908
1951
  );
@@ -1911,16 +1954,17 @@ var Schematic = class {
1911
1954
  }
1912
1955
  }, this.webSocketConnectionTimeout);
1913
1956
  webSocket.onopen = () => {
1957
+ if (isResolved) return;
1914
1958
  isResolved = true;
1915
1959
  if (timeoutId !== null) {
1916
1960
  clearTimeout(timeoutId);
1917
1961
  }
1918
- this.wsReconnectAttempts = 0;
1919
1962
  this.wsIntentionalDisconnect = false;
1920
1963
  this.debug(`WebSocket connection ${connectionId} opened successfully`);
1921
1964
  resolve(webSocket);
1922
1965
  };
1923
1966
  webSocket.onerror = (error) => {
1967
+ if (isResolved) return;
1924
1968
  isResolved = true;
1925
1969
  if (timeoutId !== null) {
1926
1970
  clearTimeout(timeoutId);
@@ -1929,7 +1973,6 @@ var Schematic = class {
1929
1973
  reject(error);
1930
1974
  };
1931
1975
  webSocket.onclose = () => {
1932
- isResolved = true;
1933
1976
  if (timeoutId !== null) {
1934
1977
  clearTimeout(timeoutId);
1935
1978
  }
@@ -1939,7 +1982,7 @@ var Schematic = class {
1939
1982
  this.currentWebSocket = null;
1940
1983
  this.isConnecting = false;
1941
1984
  }
1942
- if (!this.wsIntentionalDisconnect && this.webSocketReconnect) {
1985
+ if (!isResolved && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
1943
1986
  this.attemptReconnect();
1944
1987
  }
1945
1988
  };
@@ -2019,12 +2062,31 @@ var Schematic = class {
2019
2062
  getFlagCheck = (flagKey) => {
2020
2063
  const contextStr = contextString(this.context);
2021
2064
  const checks = this.checks[contextStr] ?? {};
2022
- return checks[flagKey];
2065
+ const check = checks[flagKey];
2066
+ if (check !== void 0) {
2067
+ return check;
2068
+ }
2069
+ if (flagKey in this.flagCheckDefaults || flagKey in this.flagValueDefaults) {
2070
+ return this.resolveFallbackCheckFlagReturn(
2071
+ flagKey,
2072
+ void 0,
2073
+ "Default value used"
2074
+ );
2075
+ }
2076
+ return void 0;
2023
2077
  };
2024
2078
  // flagValues state
2025
2079
  getFlagValue = (flagKey) => {
2026
- const check = this.getFlagCheck(flagKey);
2027
- return check?.value;
2080
+ const contextStr = contextString(this.context);
2081
+ const checks = this.checks[contextStr] ?? {};
2082
+ const check = checks[flagKey];
2083
+ if (check?.value !== void 0) {
2084
+ return check.value;
2085
+ }
2086
+ if (flagKey in this.flagCheckDefaults || flagKey in this.flagValueDefaults) {
2087
+ return this.resolveFallbackValue(flagKey);
2088
+ }
2089
+ return void 0;
2028
2090
  };
2029
2091
  /** Register an event listener that will be notified with the boolean value for a given flag when this value changes */
2030
2092
  addFlagValueListener = (flagKey, listener) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematichq/schematic-js",
3
- "version": "1.2.12",
3
+ "version": "1.2.14",
4
4
  "main": "dist/schematic.cjs.js",
5
5
  "module": "dist/schematic.esm.js",
6
6
  "types": "dist/schematic.d.ts",
@@ -41,14 +41,14 @@
41
41
  "@microsoft/api-extractor": "^7.55.0",
42
42
  "@openapitools/openapi-generator-cli": "^2.25.2",
43
43
  "@vitest/browser": "^4.0.10",
44
- "esbuild": "^0.27.0",
44
+ "esbuild": "^0.27.1",
45
45
  "eslint": "^9.39.1",
46
46
  "globals": "^16.5.0",
47
47
  "happy-dom": "^20.0.10",
48
48
  "husky": "^9.1.7",
49
49
  "jsdom": "^27.2.0",
50
50
  "mock-socket": "^9.3.1",
51
- "prettier": "^3.6.2",
51
+ "prettier": "^3.7.4",
52
52
  "typescript": "^5.9.3",
53
53
  "typescript-eslint": "^8.47.0",
54
54
  "vitest": "^4.0.10"