@schematichq/schematic-js 1.2.21 → 1.3.0

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 se=Object.create;var Q=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ce=Object.getPrototypeOf,le=Object.prototype.hasOwnProperty;var ue=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports);var de=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of oe(e))!le.call(n,r)&&r!==t&&Q(n,r,{get:()=>e[r],enumerable:!(i=ae(e,r))||i.enumerable});return n};var fe=(n,e,t)=>(t=n!=null?se(ce(n)):{},de(e||!n||!n.__esModule?Q(t,"default",{value:n,enumerable:!0}):t,n));var G=ue(z=>{(function(n){var e=(function(t){var i=typeof globalThis<"u"&&globalThis||typeof n<"u"&&n||typeof global<"u"&&global||{},r={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(s){return s&&DataView.prototype.isPrototypeOf(s)}if(r.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(s){return s&&o.indexOf(Object.prototype.toString.call(s))>-1};function f(s){if(typeof s!="string"&&(s=String(s)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(s)||s==="")throw new TypeError('Invalid character in header field name: "'+s+'"');return s.toLowerCase()}function h(s){return typeof s!="string"&&(s=String(s)),s}function k(s){var a={next:function(){var u=s.shift();return{done:u===void 0,value:u}}};return r.iterable&&(a[Symbol.iterator]=function(){return a}),a}function g(s){this.map={},s instanceof g?s.forEach(function(a,u){this.append(u,a)},this):Array.isArray(s)?s.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):s&&Object.getOwnPropertyNames(s).forEach(function(a){this.append(a,s[a])},this)}g.prototype.append=function(s,a){s=f(s),a=h(a);var u=this.map[s];this.map[s]=u?u+", "+a:a},g.prototype.delete=function(s){delete this.map[f(s)]},g.prototype.get=function(s){return s=f(s),this.has(s)?this.map[s]:null},g.prototype.has=function(s){return this.map.hasOwnProperty(f(s))},g.prototype.set=function(s,a){this.map[f(s)]=h(a)},g.prototype.forEach=function(s,a){for(var u in this.map)this.map.hasOwnProperty(u)&&s.call(a,this.map[u],u,this)},g.prototype.keys=function(){var s=[];return this.forEach(function(a,u){s.push(u)}),k(s)},g.prototype.values=function(){var s=[];return this.forEach(function(a){s.push(a)}),k(s)},g.prototype.entries=function(){var s=[];return this.forEach(function(a,u){s.push([u,a])}),k(s)},r.iterable&&(g.prototype[Symbol.iterator]=g.prototype.entries);function v(s){if(!s._noBody){if(s.bodyUsed)return Promise.reject(new TypeError("Already read"));s.bodyUsed=!0}}function x(s){return new Promise(function(a,u){s.onload=function(){a(s.result)},s.onerror=function(){u(s.error)}})}function T(s){var a=new FileReader,u=x(a);return a.readAsArrayBuffer(s),u}function $(s){var a=new FileReader,u=x(a),p=/charset=([A-Za-z0-9_-]+)/.exec(s.type),m=p?p[1]:"utf-8";return a.readAsText(s,m),u}function j(s){for(var a=new Uint8Array(s),u=new Array(a.length),p=0;p<a.length;p++)u[p]=String.fromCharCode(a[p]);return u.join("")}function W(s){if(s.slice)return s.slice(0);var a=new Uint8Array(s.byteLength);return a.set(new Uint8Array(s)),a.buffer}function J(){return this.bodyUsed=!1,this._initBody=function(s){this.bodyUsed=this.bodyUsed,this._bodyInit=s,s?typeof s=="string"?this._bodyText=s:r.blob&&Blob.prototype.isPrototypeOf(s)?this._bodyBlob=s:r.formData&&FormData.prototype.isPrototypeOf(s)?this._bodyFormData=s:r.searchParams&&URLSearchParams.prototype.isPrototypeOf(s)?this._bodyText=s.toString():r.arrayBuffer&&r.blob&&c(s)?(this._bodyArrayBuffer=W(s.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):r.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(s)||l(s))?this._bodyArrayBuffer=W(s):this._bodyText=s=Object.prototype.toString.call(s):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof s=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):r.searchParams&&URLSearchParams.prototype.isPrototypeOf(s)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},r.blob&&(this.blob=function(){var s=v(this);if(s)return s;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 s=v(this);return s||(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(r.blob)return this.blob().then(T);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var s=v(this);if(s)return s;if(this._bodyBlob)return $(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(j(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},r.formData&&(this.formData=function(){return this.text().then(ne)}),this.json=function(){return this.text().then(JSON.parse)},this}var ee=["CONNECT","DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT","TRACE"];function te(s){var a=s.toUpperCase();return ee.indexOf(a)>-1?a:s}function R(s,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(s instanceof R){if(s.bodyUsed)throw new TypeError("Already read");this.url=s.url,this.credentials=s.credentials,a.headers||(this.headers=new g(s.headers)),this.method=s.method,this.mode=s.mode,this.signal=s.signal,!u&&s._bodyInit!=null&&(u=s._bodyInit,s.bodyUsed=!0)}else this.url=String(s);if(this.credentials=a.credentials||this.credentials||"same-origin",(a.headers||!this.headers)&&(this.headers=new g(a.headers)),this.method=te(a.method||this.method||"GET"),this.mode=a.mode||this.mode||null,this.signal=a.signal||this.signal||(function(){if("AbortController"in i){var d=new AbortController;return d.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 p=/([?&])_=[^&]*/;if(p.test(this.url))this.url=this.url.replace(p,"$1_="+new Date().getTime());else{var m=/\?/;this.url+=(m.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}R.prototype.clone=function(){return new R(this,{body:this._bodyInit})};function ne(s){var a=new FormData;return s.trim().split("&").forEach(function(u){if(u){var p=u.split("="),m=p.shift().replace(/\+/g," "),d=p.join("=").replace(/\+/g," ");a.append(decodeURIComponent(m),decodeURIComponent(d))}}),a}function re(s){var a=new g,u=s.replace(/\r?\n[\t ]+/g," ");return u.split("\r").map(function(p){return p.indexOf(`
2
- `)===0?p.substr(1,p.length):p}).forEach(function(p){var m=p.split(":"),d=m.shift().trim();if(d){var _=m.join(":").trim();try{a.append(d,_)}catch(I){console.warn("Response "+I.message)}}}),a}J.call(R.prototype);function w(s,a){if(!(this instanceof w))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(s)}J.call(w.prototype),w.prototype.clone=function(){return new w(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new g(this.headers),url:this.url})},w.error=function(){var s=new w(null,{status:200,statusText:""});return s.ok=!1,s.status=0,s.type="error",s};var ie=[301,302,303,307,308];w.redirect=function(s,a){if(ie.indexOf(a)===-1)throw new RangeError("Invalid status code");return new w(null,{status:a,headers:{location:s}})},t.DOMException=i.DOMException;try{new t.DOMException}catch{t.DOMException=function(a,u){this.message=a,this.name=u;var p=Error(a);this.stack=p.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function O(s,a){return new Promise(function(u,p){var m=new R(s,a);if(m.signal&&m.signal.aborted)return p(new t.DOMException("Aborted","AbortError"));var d=new XMLHttpRequest;function _(){d.abort()}d.onload=function(){var y={statusText:d.statusText,headers:re(d.getAllResponseHeaders()||"")};m.url.indexOf("file://")===0&&(d.status<200||d.status>599)?y.status=200:y.status=d.status,y.url="responseURL"in d?d.responseURL:y.headers.get("X-Request-URL");var F="response"in d?d.response:d.responseText;setTimeout(function(){u(new w(F,y))},0)},d.onerror=function(){setTimeout(function(){p(new TypeError("Network request failed"))},0)},d.ontimeout=function(){setTimeout(function(){p(new TypeError("Network request timed out"))},0)},d.onabort=function(){setTimeout(function(){p(new t.DOMException("Aborted","AbortError"))},0)};function I(y){try{return y===""&&i.location.href?i.location.href:y}catch{return y}}if(d.open(m.method,I(m.url),!0),m.credentials==="include"?d.withCredentials=!0:m.credentials==="omit"&&(d.withCredentials=!1),"responseType"in d&&(r.blob?d.responseType="blob":r.arrayBuffer&&(d.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(y){q.push(f(y)),d.setRequestHeader(y,h(a.headers[y]))}),m.headers.forEach(function(y,F){q.indexOf(F)===-1&&d.setRequestHeader(F,y)})}else m.headers.forEach(function(y,F){d.setRequestHeader(F,y)});m.signal&&(m.signal.addEventListener("abort",_),d.onreadystatechange=function(){d.readyState===4&&m.signal.removeEventListener("abort",_)}),d.send(typeof m._bodyInit>"u"?null:m._bodyInit)})}return O.polyfill=!0,i.fetch||(i.fetch=O,i.Headers=g,i.Request=R,i.Response=w),t.Headers=g,t.Request=R,t.Response=w,t.fetch=O,t})({})})(typeof self<"u"?self:z)});var b=[];for(let n=0;n<256;++n)b.push((n+256).toString(16).slice(1));function H(n,e=0){return(b[n[e+0]]+b[n[e+1]]+b[n[e+2]]+b[n[e+3]]+"-"+b[n[e+4]]+b[n[e+5]]+"-"+b[n[e+6]]+b[n[e+7]]+"-"+b[n[e+8]]+b[n[e+9]]+"-"+b[n[e+10]]+b[n[e+11]]+b[n[e+12]]+b[n[e+13]]+b[n[e+14]]+b[n[e+15]]).toLowerCase()}var U,he=new Uint8Array(16);function A(){if(!U){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");U=crypto.getRandomValues.bind(crypto)}return U(he)}var ge=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),P={randomUUID:ge};function pe(n,e,t){n=n||{};let i=n.random??n.rng?.()??A();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 r=0;r<16;++r)e[t+r]=i[r];return e}return H(i)}function me(n,e,t){return P.randomUUID&&!e&&!n?P.randomUUID():pe(n,e,t)}var S=me;var bt=fe(G());function X(n){return ye(n,!1)}function ye(n,e){return n}function K(n){return be(n,!1)}function be(n,e){return n==null?n:{allocation:n.allocation==null?void 0:n.allocation,creditId:n.credit_id==null?void 0:n.credit_id,creditRemaining:n.credit_remaining==null?void 0:n.credit_remaining,creditTotal:n.credit_total==null?void 0:n.credit_total,creditUsed:n.credit_used==null?void 0:n.credit_used,eventName:n.event_name==null?void 0:n.event_name,featureId:n.feature_id,featureKey:n.feature_key,metricPeriod:n.metric_period==null?void 0:n.metric_period,metricResetAt:n.metric_reset_at==null?void 0:new Date(n.metric_reset_at),monthReset:n.month_reset==null?void 0:n.month_reset,softLimit:n.soft_limit==null?void 0:n.soft_limit,usage:n.usage==null?void 0:n.usage,valueType:X(n.value_type)}}function C(n){return ke(n,!1)}function ke(n,e){return n==null?n:{companyId:n.company_id==null?void 0:n.company_id,entitlement:n.entitlement==null?void 0:K(n.entitlement),error:n.error==null?void 0:n.error,featureAllocation:n.feature_allocation==null?void 0:n.feature_allocation,featureUsage:n.feature_usage==null?void 0:n.feature_usage,featureUsageEvent:n.feature_usage_event==null?void 0:n.feature_usage_event,featureUsagePeriod:n.feature_usage_period==null?void 0:n.feature_usage_period,featureUsageResetAt:n.feature_usage_reset_at==null?void 0:new Date(n.feature_usage_reset_at),flag:n.flag,flagId:n.flag_id==null?void 0:n.flag_id,reason:n.reason,ruleId:n.rule_id==null?void 0:n.rule_id,ruleType:n.rule_type==null?void 0:n.rule_type,userId:n.user_id==null?void 0:n.user_id,value:n.value}}function N(n){return ve(n,!1)}function ve(n,e=!1){return n==null?n:{company_id:n.companyId,error:n.error,flag_id:n.flagId,flag_key:n.flagKey,reason:n.reason,req_company:n.reqCompany,req_user:n.reqUser,rule_id:n.ruleId,user_id:n.userId,value:n.value}}function L(n){return we(n,!1)}function we(n,e){return n==null?n:{data:C(n.data),params:n.params}}function Y(n){return Re(n,!1)}function Re(n,e){return n==null?n:{flags:n.flags.map(C)}}function B(n){return Fe(n,!1)}function Fe(n,e){return n==null?n:{data:Y(n.data),params:n.params}}var M=n=>{let{companyId:e,error:t,featureAllocation:i,featureUsage:r,featureUsageEvent:c,featureUsagePeriod:o,featureUsageResetAt:l,flag:f,flagId:h,reason:k,ruleId:g,ruleType:v,userId:x,value:T}=C(n);return{featureUsageExceeded:!T&&(v=="company_override_usage_exceeded"||v=="plan_entitlement_usage_exceeded"),companyId:e??void 0,error:t??void 0,featureAllocation:i??void 0,featureUsage:r??void 0,featureUsageEvent:c===null?void 0:c,featureUsagePeriod:o??void 0,featureUsageResetAt:l??void 0,flag:f,flagId:h??void 0,reason:k,ruleId:g??void 0,ruleType:v??void 0,userId:x??void 0,value:T}};function E(n){let e=Object.keys(n).reduce((t,i)=>{let c=Object.keys(n[i]||{}).sort().reduce((o,l)=>(o[l]=n[i][l],o),{});return t[i]=c,t},{});return JSON.stringify(e)}var V="1.2.21";var Z="schematicId";var D=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={};fallbackCheckCache={};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),r=i.get("schematic_debug");r!==null&&(r===""||r==="true"||r==="1")&&(this.debugEnabled=!0);let c=i.get("schematic_offline");c!==null&&(c===""||c==="true"||c==="1")&&(this.offlineEnabled=!0,this.debugEnabled=!0)}if(this.offlineEnabled&&t?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${V}`,...t?.additionalHeaders??{}},t?.storage)this.storage=t.storage;else try{typeof localStorage<"u"&&(this.storage=localStorage)}catch{}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,i="Fallback value used",r){if(t!==void 0)return{flag:e,value:t,reason:i,error:r};if(e in this.flagCheckDefaults){let c=this.flagCheckDefaults[e];return{...c,flag:e,reason:r!==void 0?i:c.reason,error:r}}return e in this.flagValueDefaults?{flag:e,value:this.flagValueDefaults[e],reason:i,error:r}:{flag:e,value:!1,reason:i,error:r}}async checkFlag(e){let{fallback:t,key:i}=e,r=e.context||this.context,c=E(r);if(this.debug(`checkFlag: ${i}`,{context:r,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(r)}).then(l=>{if(!l.ok)throw new Error("Network response was not ok");return l.json()}).then(l=>{let f=L(l);this.debug(`checkFlag result: ${i}`,f);let h=M(f.data);return typeof h.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(h),this.submitFlagCheckEvent(i,h,r),h.value}).catch(l=>{console.warn("There was a problem with the fetch operation:",l);let f=this.resolveFallbackCheckFlagReturn(i,t,"API request failed",l instanceof Error?l.message:String(l));return this.submitFlagCheckEvent(i,f,r),f.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(r)}catch(k){console.warn("WebSocket connection failed, using fallback value:",k);let g=this.resolveFallbackCheckFlagReturn(i,t,"WebSocket connection failed",k instanceof Error?k.message:String(k));return this.submitFlagCheckEvent(i,g,r),g.value}let f=(this.checks[c]??{})[i],h=f?.value??this.resolveFallbackValue(i,t);if(this.debug(`checkFlag WebSocket result: ${i}`,typeof f<"u"?f:{value:h,fallbackUsed:!0}),typeof f<"u")this.submitFlagCheckEvent(i,f,r);else{let k=this.resolveFallbackCheckFlagReturn(i,t,"No flag values available");this.submitFlagCheckEvent(i,k,r)}return h}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,r),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),E(e)in this.checks||(this.checks[E(e)]={}),(i.flags??[]).forEach(r=>{let c=M(r),o=E(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[r.flag]?.size??0)>0||(this.flagValueListeners[r.flag]?.size??0)>0)&&this.submitFlagCheckEvent(c.flag,c,e),this.debug(`About to notify listeners for flag ${r.flag}`,{flag:r.flag,value:c.value}),this.notifyFlagCheckListeners(r.flag,c),this.notifyFlagValueListeners(r.flag,c.value),this.debug(`Finished notifying listeners for flag ${r.flag}`,{flag:r.flag,value:c.value})}),this.flushContextDependentEventQueue(),this.setIsPending(!1)}}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(e,t,i){let r={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:",r),this.handleEvent("flag_check",N(r))}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(r=>{if(!r.ok)throw new Error("Network response was not ok");return r.json()}).then(r=>{let c=B(r);return this.debug("checkFlags result:",c),(c?.data?.flags??[]).reduce((o,l)=>(o[l.flag]=l.value,o),{})}).catch(r=>(console.warn("There was a problem with the fetch operation:",r),{}))};identify=e=>(this.debug("identify:",e),this.setContext({company:e.company?.keys,user:e.keys}).catch(t=>{console.warn("Error setting context:",t)}),this.handleEvent("identify",e));setContext=async e=>{if(this.isOffline()||!this.useWebSocket)return this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),Promise.resolve();if(!(E(e)===E(this.context)&&this.conn!==null&&!this.isPending))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){console.warn("Failed to establish WebSocket connection:",t),this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),this.attemptReconnect()}};track=e=>{let{company:t,user:i,event:r,traits:c,quantity:o=1}=e;if(!this.hasContext(t,i)){this.debug(`track: queuing event "${r}" until context is available`);let f={api_key:this.apiKey,body:{company:t,event:r,traits:c??{},user:i,quantity:o},sent_at:new Date().toISOString(),tracker_event_id:S(),tracker_user_id:this.getAnonymousId(),type:"track"};return this.contextDependentEventQueue.push(f),Promise.resolve()}let l={company:t??this.context.company,event:r,traits:c??{},user:i??this.context.user,quantity:o};return this.debug("track:",l),r in this.featureUsageEventMap&&this.optimisticallyUpdateFeatureUsage(r,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(([r,c])=>{if(c===void 0)return;let o={...c};if(typeof o.featureUsage=="number"){if(o.featureUsage+=t,typeof o.featureAllocation=="number"){let f=o.featureUsageExceeded===!0,h=o.featureUsage>=o.featureAllocation;h!==f&&(o.featureUsageExceeded=h,h&&(o.value=!1),this.debug(`Usage limit status changed for flag: ${r}`,{was:f?"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][r]=o);let l=E(this.context);this.checks[l]!==void 0&&this.checks[l]!==null&&(this.checks[l][r]=o),this.notifyFlagCheckListeners(r,o),this.notifyFlagValueListeners(r,o.value)}}))};hasContext=(e,t)=>{let i=e!=null&&Object.keys(e).length>0||t!=null&&Object.keys(t).length>0,r=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||r};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},r={...e,body:i,sent_at:new Date().toISOString()};this.sendEvent(r)}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 r of this.eventQueue)r.next_retry_at===void 0||r.next_retry_at<=e?t.push(r):i.push(r);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 r of t)try{await this.sendEvent(r),this.debug("Queued event sent successfully:",r.type)}catch(c){this.debug("Failed to send queued event:",c)}};getAnonymousId=()=>{if(!this.storage)return S();let e=this.storage.getItem(Z);if(typeof e<"u")return e;let t=S();return this.storage.setItem(Z,t),t};handleEvent=(e,t)=>{let i={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:S(),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 r=await fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:i});if(!r.ok)throw new Error(`HTTP ${r.status}: ${r.statusText}`);this.debug("event sent:",{status:r.status,statusText:r.statusText})}catch(r){let c=(e.retry_count??0)+1;if(c<=this.maxEventRetries){this.debug(`Event failed to send (attempt ${c}/${this.maxEventRetries}), queueing for retry:`,r);let o=this.eventRetryInitialDelay*Math.pow(2,c-1),l=Math.min(o,this.eventRetryMaxDelay),f=Date.now()+l,h={...e,retry_count:c,next_retry_at:f};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:`,r)}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 r=await this.conn;this.currentWebSocket===r&&(this.currentWebSocket=null),(r.readyState===WebSocket.OPEN||r.readyState===WebSocket.CONNECTING)&&r.close()}catch(r){this.debug(`${i}: error closing existing connection:`,r)}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 r=await this.conn;this.isConnecting=!1,await this.wsSendMessage(r,this.context,!0),this.debug(`${i}: reconnection successful`)}catch(r){this.isConnecting=!1,this.debug(`${i}: reconnection failed:`,r)}}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.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),i=Math.random()*t*.5,r=t+i;return this.debug(`Reconnect delay calculated: ${r.toFixed(0)}ms (attempt ${this.wsReconnectAttempts+1}/${this.webSocketMaxReconnectAttempts})`),r};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=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 i=0;i<t;i++)try{let r=await this.wsConnectOnce();return this.wsReconnectAttempts=0,r}catch(r){if(e=r instanceof Error?r:new Error(String(r)),!(e.message==="WebSocket connection timeout"))throw this.debug("WebSocket connection failed with non-timeout error, not retrying:",e.message),e;if(i<t-1){let o=this.webSocketInitialRetryDelay*Math.pow(2,i),l=Math.min(o,this.webSocketMaxRetryDelay),f=l*.2*Math.random(),h=l+f;this.debug(`WebSocket connection timeout (attempt ${i+1}/${t}), retrying in ${h.toFixed(0)}ms`),await new Promise(k=>setTimeout(k,h))}else this.debug(`WebSocket connection timeout (attempt ${i+1}/${t}), no more retries`)}throw e??new Error("WebSocket connection failed")};wsConnectOnce=()=>new Promise((e,t)=>{let i=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",i);let r=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||(l=!0,this.debug(`WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`),r.close(),t(new Error("WebSocket connection timeout")))},this.webSocketConnectionTimeout),r.onopen=()=>{l||(l=!0,o!==null&&clearTimeout(o),this.wsIntentionalDisconnect=!1,this.debug(`WebSocket connection ${c} opened successfully`),e(r))},r.onerror=f=>{if(l)return;l=!0,o!==null&&clearTimeout(o),this.debug(`WebSocket connection ${c} error:`,f);let h=new Error("WebSocket connection failed during handshake");t(h)},r.onclose=()=>{o!==null&&clearTimeout(o),this.debug(`WebSocket connection ${c} closed`),this.conn=null,this.currentWebSocket===r&&(this.currentWebSocket=null,this.isConnecting=!1),!l&&!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((r,c)=>{if(!i&&E(t)==E(this.context))return this.debug("WebSocket context unchanged, skipping update"),r(this.setIsPending(!1));this.debug(i?"WebSocket force sending context (reconnection):":"WebSocket context updated:",t),this.context=t;let o=()=>{let l=!1,f=this.createPersistentMessageHandler(t),h=v=>{f(v),l||(l=!0,r())};e.addEventListener("message",h),e.addEventListener("close",v=>{l||(l=!0,v.code===4001?c(new Error(`Authentication failed: ${v.reason!==""?v.reason:"Invalid API key"}`)):c(new Error("WebSocket connection closed unexpectedly")))}),this.currentWebSocket=e;let k=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${V}`,g={apiKey:this.apiKey,clientVersion:k,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.isPending=e,this.isPendingListeners.forEach(t=>Se(t,e)))};getFlagCheck=e=>{let t=E(this.context),r=(this.checks[t]??{})[e];if(r!==void 0)return r;if(e in this.flagCheckDefaults||e in this.flagValueDefaults)return e in this.fallbackCheckCache||(this.fallbackCheckCache[e]=this.resolveFallbackCheckFlagReturn(e,void 0,"Default value used")),this.fallbackCheckCache[e]};getFlagValue=e=>{let t=E(this.context),r=(this.checks[t]??{})[e];if(r?.value!==void 0)return r.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 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(r=>Ce(r,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((r,c)=>{this.debug(`Calling listener ${c} for flag ${e}`,{flagKey:e,value:t}),xe(r,t),this.debug(`Listener ${c} for flag ${e} completed`,{flagKey:e,value:t})})}},Se=(n,e)=>{n.length>0?n(e):n()},Ce=(n,e)=>{n.length>0?n(e):n()},xe=(n,e)=>{n.length>0?n(e):n()};window.Schematic=D;})();
1
+ "use strict";(()=>{var we=Object.create;var ne=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var Ve=Object.getOwnPropertyNames;var Ue=Object.getPrototypeOf,Ge=Object.prototype.hasOwnProperty;var ve=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports);var Le=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ve(e))!Ge.call(n,r)&&r!==t&&ne(n,r,{get:()=>e[r],enumerable:!(i=Ie(e,r))||i.enumerable});return n};var Me=(n,e,t)=>(t=n!=null?we(Ue(n)):{},Le(e||!n||!n.__esModule?ne(t,"default",{value:n,enumerable:!0}):t,n));var ae=ve(re=>{(function(n){var e=(function(t){var i=typeof globalThis<"u"&&globalThis||typeof n<"u"&&n||typeof global<"u"&&global||{},r={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 s(a){return a&&DataView.prototype.isPrototypeOf(a)}if(r.arrayBuffer)var l=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],p=ArrayBuffer.isView||function(a){return a&&l.indexOf(Object.prototype.toString.call(a))>-1};function u(a){if(typeof a!="string"&&(a=String(a)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(a)||a==="")throw new TypeError('Invalid character in header field name: "'+a+'"');return a.toLowerCase()}function f(a){return typeof a!="string"&&(a=String(a)),a}function D(a){var o={next:function(){var d=a.shift();return{done:d===void 0,value:d}}};return r.iterable&&(o[Symbol.iterator]=function(){return o}),o}function y(a){this.map={},a instanceof y?a.forEach(function(o,d){this.append(d,o)},this):Array.isArray(a)?a.forEach(function(o){if(o.length!=2)throw new TypeError("Headers constructor: expected name/value pair to be length 2, found"+o.length);this.append(o[0],o[1])},this):a&&Object.getOwnPropertyNames(a).forEach(function(o){this.append(o,a[o])},this)}y.prototype.append=function(a,o){a=u(a),o=f(o);var d=this.map[a];this.map[a]=d?d+", "+o:o},y.prototype.delete=function(a){delete this.map[u(a)]},y.prototype.get=function(a){return a=u(a),this.has(a)?this.map[a]:null},y.prototype.has=function(a){return this.map.hasOwnProperty(u(a))},y.prototype.set=function(a,o){this.map[u(a)]=f(o)},y.prototype.forEach=function(a,o){for(var d in this.map)this.map.hasOwnProperty(d)&&a.call(o,this.map[d],d,this)},y.prototype.keys=function(){var a=[];return this.forEach(function(o,d){a.push(d)}),D(a)},y.prototype.values=function(){var a=[];return this.forEach(function(o){a.push(o)}),D(a)},y.prototype.entries=function(){var a=[];return this.forEach(function(o,d){a.push([d,o])}),D(a)},r.iterable&&(y.prototype[Symbol.iterator]=y.prototype.entries);function S(a){if(!a._noBody){if(a.bodyUsed)return Promise.reject(new TypeError("Already read"));a.bodyUsed=!0}}function x(a){return new Promise(function(o,d){a.onload=function(){o(a.result)},a.onerror=function(){d(a.error)}})}function N(a){var o=new FileReader,d=x(o);return o.readAsArrayBuffer(a),d}function Y(a){var o=new FileReader,d=x(o),m=/charset=([A-Za-z0-9_-]+)/.exec(a.type),g=m?m[1]:"utf-8";return o.readAsText(a,g),d}function Ne(a){for(var o=new Uint8Array(a),d=new Array(o.length),m=0;m<o.length;m++)d[m]=String.fromCharCode(o[m]);return d.join("")}function j(a){if(a.slice)return a.slice(0);var o=new Uint8Array(a.byteLength);return o.set(new Uint8Array(a)),o.buffer}function ee(){return this.bodyUsed=!1,this._initBody=function(a){this.bodyUsed=this.bodyUsed,this._bodyInit=a,a?typeof a=="string"?this._bodyText=a:r.blob&&Blob.prototype.isPrototypeOf(a)?this._bodyBlob=a:r.formData&&FormData.prototype.isPrototypeOf(a)?this._bodyFormData=a:r.searchParams&&URLSearchParams.prototype.isPrototypeOf(a)?this._bodyText=a.toString():r.arrayBuffer&&r.blob&&s(a)?(this._bodyArrayBuffer=j(a.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):r.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(a)||p(a))?this._bodyArrayBuffer=j(a):this._bodyText=a=Object.prototype.toString.call(a):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof a=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):r.searchParams&&URLSearchParams.prototype.isPrototypeOf(a)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},r.blob&&(this.blob=function(){var a=S(this);if(a)return a;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 a=S(this);return a||(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(r.blob)return this.blob().then(N);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var a=S(this);if(a)return a;if(this._bodyBlob)return Y(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(Ne(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},r.formData&&(this.formData=function(){return this.text().then(Je)}),this.json=function(){return this.text().then(JSON.parse)},this}var Fe=["CONNECT","DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT","TRACE"];function Ee(a){var o=a.toUpperCase();return Fe.indexOf(o)>-1?o:a}function P(a,o){if(!(this instanceof P))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');o=o||{};var d=o.body;if(a instanceof P){if(a.bodyUsed)throw new TypeError("Already read");this.url=a.url,this.credentials=a.credentials,o.headers||(this.headers=new y(a.headers)),this.method=a.method,this.mode=a.mode,this.signal=a.signal,!d&&a._bodyInit!=null&&(d=a._bodyInit,a.bodyUsed=!0)}else this.url=String(a);if(this.credentials=o.credentials||this.credentials||"same-origin",(o.headers||!this.headers)&&(this.headers=new y(o.headers)),this.method=Ee(o.method||this.method||"GET"),this.mode=o.mode||this.mode||null,this.signal=o.signal||this.signal||(function(){if("AbortController"in i){var c=new AbortController;return c.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")&&(o.cache==="no-store"||o.cache==="no-cache")){var m=/([?&])_=[^&]*/;if(m.test(this.url))this.url=this.url.replace(m,"$1_="+new Date().getTime());else{var g=/\?/;this.url+=(g.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}P.prototype.clone=function(){return new P(this,{body:this._bodyInit})};function Je(a){var o=new FormData;return a.trim().split("&").forEach(function(d){if(d){var m=d.split("="),g=m.shift().replace(/\+/g," "),c=m.join("=").replace(/\+/g," ");o.append(decodeURIComponent(g),decodeURIComponent(c))}}),o}function ke(a){var o=new y,d=a.replace(/\r?\n[\t ]+/g," ");return d.split("\r").map(function(m){return m.indexOf(`
2
+ `)===0?m.substr(1,m.length):m}).forEach(function(m){var g=m.split(":"),c=g.shift().trim();if(c){var F=g.join(":").trim();try{o.append(c,F)}catch(v){console.warn("Response "+v.message)}}}),o}ee.call(P.prototype);function R(a,o){if(!(this instanceof R))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(o||(o={}),this.type="default",this.status=o.status===void 0?200:o.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=o.statusText===void 0?"":""+o.statusText,this.headers=new y(o.headers),this.url=o.url||"",this._initBody(a)}ee.call(R.prototype),R.prototype.clone=function(){return new R(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new y(this.headers),url:this.url})},R.error=function(){var a=new R(null,{status:200,statusText:""});return a.ok=!1,a.status=0,a.type="error",a};var Ae=[301,302,303,307,308];R.redirect=function(a,o){if(Ae.indexOf(o)===-1)throw new RangeError("Invalid status code");return new R(null,{status:o,headers:{location:a}})},t.DOMException=i.DOMException;try{new t.DOMException}catch{t.DOMException=function(o,d){this.message=o,this.name=d;var m=Error(o);this.stack=m.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function G(a,o){return new Promise(function(d,m){var g=new P(a,o);if(g.signal&&g.signal.aborted)return m(new t.DOMException("Aborted","AbortError"));var c=new XMLHttpRequest;function F(){c.abort()}c.onload=function(){var T={statusText:c.statusText,headers:ke(c.getAllResponseHeaders()||"")};g.url.indexOf("file://")===0&&(c.status<200||c.status>599)?T.status=200:T.status=c.status,T.url="responseURL"in c?c.responseURL:T.headers.get("X-Request-URL");var b="response"in c?c.response:c.responseText;setTimeout(function(){d(new R(b,T))},0)},c.onerror=function(){setTimeout(function(){m(new TypeError("Network request failed"))},0)},c.ontimeout=function(){setTimeout(function(){m(new TypeError("Network request timed out"))},0)},c.onabort=function(){setTimeout(function(){m(new t.DOMException("Aborted","AbortError"))},0)};function v(T){try{return T===""&&i.location.href?i.location.href:T}catch{return T}}if(c.open(g.method,v(g.url),!0),g.credentials==="include"?c.withCredentials=!0:g.credentials==="omit"&&(c.withCredentials=!1),"responseType"in c&&(r.blob?c.responseType="blob":r.arrayBuffer&&(c.responseType="arraybuffer")),o&&typeof o.headers=="object"&&!(o.headers instanceof y||i.Headers&&o.headers instanceof i.Headers)){var te=[];Object.getOwnPropertyNames(o.headers).forEach(function(T){te.push(u(T)),c.setRequestHeader(T,f(o.headers[T]))}),g.headers.forEach(function(T,b){te.indexOf(b)===-1&&c.setRequestHeader(b,T)})}else g.headers.forEach(function(T,b){c.setRequestHeader(b,T)});g.signal&&(g.signal.addEventListener("abort",F),c.onreadystatechange=function(){c.readyState===4&&g.signal.removeEventListener("abort",F)}),c.send(typeof g._bodyInit>"u"?null:g._bodyInit)})}return G.polyfill=!0,i.fetch||(i.fetch=G,i.Headers=y,i.Request=P,i.Response=R),t.Headers=y,t.Request=P,t.Response=R,t.fetch=G,t})({})})(typeof self<"u"?self:re)});var h=[];for(let n=0;n<256;++n)h.push((n+256).toString(16).slice(1));function ie(n,e=0){return(h[n[e+0]]+h[n[e+1]]+h[n[e+2]]+h[n[e+3]]+"-"+h[n[e+4]]+h[n[e+5]]+"-"+h[n[e+6]]+h[n[e+7]]+"-"+h[n[e+8]]+h[n[e+9]]+"-"+h[n[e+10]]+h[n[e+11]]+h[n[e+12]]+h[n[e+13]]+h[n[e+14]]+h[n[e+15]]).toLowerCase()}var L,qe=new Uint8Array(16);function M(){if(!L){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");L=crypto.getRandomValues.bind(crypto)}return L(qe)}var We=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),q={randomUUID:We};function $e(n,e,t){n=n||{};let i=n.random??n.rng?.()??M();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 r=0;r<16;++r)e[t+r]=i[r];return e}return ie(i)}function ze(n,e,t){return q.randomUUID&&!e&&!n?q.randomUUID():$e(n,e,t)}var O=ze;var oy=Me(ae());function z(n){return mt(n,!1)}function mt(n,e){return n}function ue(n){return Tt(n,!1)}function Tt(n,e){return n==null?n:{allocation:n.allocation==null?void 0:n.allocation,creditId:n.credit_id==null?void 0:n.credit_id,creditRemaining:n.credit_remaining==null?void 0:n.credit_remaining,creditTotal:n.credit_total==null?void 0:n.credit_total,creditUsed:n.credit_used==null?void 0:n.credit_used,eventName:n.event_name==null?void 0:n.event_name,featureId:n.feature_id,featureKey:n.feature_key,metricPeriod:n.metric_period==null?void 0:n.metric_period,metricResetAt:n.metric_reset_at==null?void 0:new Date(n.metric_reset_at),monthReset:n.month_reset==null?void 0:n.month_reset,softLimit:n.soft_limit==null?void 0:n.soft_limit,usage:n.usage==null?void 0:n.usage,valueType:z(n.value_type)}}function C(n){return ht(n,!1)}function ht(n,e){return n==null?n:{companyId:n.company_id==null?void 0:n.company_id,entitlement:n.entitlement==null?void 0:ue(n.entitlement),error:n.error==null?void 0:n.error,featureAllocation:n.feature_allocation==null?void 0:n.feature_allocation,featureUsage:n.feature_usage==null?void 0:n.feature_usage,featureUsageEvent:n.feature_usage_event==null?void 0:n.feature_usage_event,featureUsagePeriod:n.feature_usage_period==null?void 0:n.feature_usage_period,featureUsageResetAt:n.feature_usage_reset_at==null?void 0:new Date(n.feature_usage_reset_at),flag:n.flag,flagId:n.flag_id==null?void 0:n.flag_id,reason:n.reason,ruleId:n.rule_id==null?void 0:n.rule_id,ruleType:n.rule_type==null?void 0:n.rule_type,userId:n.user_id==null?void 0:n.user_id,value:n.value}}function H(n){return St(n,!1)}function St(n,e){return n==null?n:{data:C(n.data),params:n.params}}function B(n){return _t(n,!1)}function _t(n,e){return n==null?n:{id:n.id,name:n.name,trialEndDate:n.trial_end_date==null?void 0:new Date(n.trial_end_date)}}function Q(n){return Rt(n,!1)}function Rt(n,e){return n==null?n:{flags:n.flags.map(C),plan:n.plan==null?void 0:B(n.plan)}}function K(n){return bt(n,!1)}function bt(n,e){return n==null?n:{data:Q(n.data),params:n.params}}function w(n){return Ot(n,!1)}function Ot(n,e=!1){return n==null?n:{company_id:n.companyId,error:n.error,flag_id:n.flagId,flag_key:n.flagKey,reason:n.reason,req_company:n.reqCompany,req_user:n.reqUser,rule_id:n.ruleId,user_id:n.userId,value:n.value}}var X=n=>{let{companyId:e,error:t,featureAllocation:i,featureUsage:r,featureUsageEvent:s,featureUsagePeriod:l,featureUsageResetAt:p,flag:u,flagId:f,reason:D,ruleId:y,ruleType:S,userId:x,value:N}=C(n);return{featureUsageExceeded:!N&&(S=="company_override_usage_exceeded"||S=="plan_entitlement_usage_exceeded"),companyId:e??void 0,error:t??void 0,featureAllocation:i??void 0,featureUsage:r??void 0,featureUsageEvent:s===null?void 0:s,featureUsagePeriod:l??void 0,featureUsageResetAt:p??void 0,flag:u,flagId:f??void 0,reason:D,ruleId:y??void 0,ruleType:S??void 0,userId:x??void 0,value:N}},xe=n=>{let{id:e,name:t,trialEndDate:i}=B(n);return{id:e,name:t,trialEndDate:i??void 0}};function _(n){let e=Object.keys(n).reduce((t,i)=>{let s=Object.keys(n[i]||{}).sort().reduce((l,p)=>(l[p]=n[i][p],l),{});return t[i]=s,t},{});return JSON.stringify(e)}var Z="1.3.0";var Be="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;planListeners=new Set;storage;useWebSocket=!1;checks={};featureUsageEventMap={};planChecks={};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={};fallbackCheckCache={};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),r=i.get("schematic_debug");r!==null&&(r===""||r==="true"||r==="1")&&(this.debugEnabled=!0);let s=i.get("schematic_offline");s!==null&&(s===""||s==="true"||s==="1")&&(this.offlineEnabled=!0,this.debugEnabled=!0)}if(this.offlineEnabled&&t?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${Z}`,...t?.additionalHeaders??{}},t?.storage)this.storage=t.storage;else try{typeof localStorage<"u"&&(this.storage=localStorage)}catch{}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,i="Fallback value used",r){if(t!==void 0)return{flag:e,value:t,reason:i,error:r};if(e in this.flagCheckDefaults){let s=this.flagCheckDefaults[e];return{...s,flag:e,reason:r!==void 0?i:s.reason,error:r}}return e in this.flagValueDefaults?{flag:e,value:this.flagValueDefaults[e],reason:i,error:r}:{flag:e,value:!1,reason:i,error:r}}async checkFlag(e){let{fallback:t,key:i}=e,r=e.context||this.context,s=_(r);if(this.debug(`checkFlag: ${i}`,{context:r,fallback:t}),this.isOffline()){let l=this.resolveFallbackCheckFlagReturn(i,t,"Offline mode - using initialization defaults");return this.debug(`checkFlag offline result: ${i}`,{value:l.value,offlineMode:!0}),l.value}if(!this.useWebSocket){let l=`${this.apiUrl}/flags/${i}/check`;return fetch(l,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(r)}).then(p=>{if(!p.ok)throw new Error("Network response was not ok");return p.json()}).then(p=>{let u=H(p);this.debug(`checkFlag result: ${i}`,u);let f=X(u.data);return typeof f.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(f),this.submitFlagCheckEvent(i,f,r),f.value}).catch(p=>{console.warn("There was a problem with the fetch operation:",p);let u=this.resolveFallbackCheckFlagReturn(i,t,"API request failed",p instanceof Error?p.message:String(p));return this.submitFlagCheckEvent(i,u,r),u.value})}try{let l=this.checks[s];if(this.conn!==null&&typeof l<"u"&&typeof l[i]<"u")return this.debug(`checkFlag cached result: ${i}`,l[i]),l[i].value;if(this.isOffline())return this.resolveFallbackValue(i,t);try{await this.setContext(r)}catch(D){console.warn("WebSocket connection failed, using fallback value:",D);let y=this.resolveFallbackCheckFlagReturn(i,t,"WebSocket connection failed",D instanceof Error?D.message:String(D));return this.submitFlagCheckEvent(i,y,r),y.value}let u=(this.checks[s]??{})[i],f=u?.value??this.resolveFallbackValue(i,t);if(this.debug(`checkFlag WebSocket result: ${i}`,typeof u<"u"?u:{value:f,fallbackUsed:!0}),typeof u<"u")this.submitFlagCheckEvent(i,u,r);else{let D=this.resolveFallbackCheckFlagReturn(i,t,"No flag values available");this.submitFlagCheckEvent(i,D,r)}return f}catch(l){console.error("Unexpected error in checkFlag:",l);let p=this.resolveFallbackCheckFlagReturn(i,t,"Unexpected error in flag check",l instanceof Error?l.message:String(l));return this.submitFlagCheckEvent(i,p,r),p.value}}debug(e,...t){this.debugEnabled&&console.log(`[Schematic] ${e}`,...t)}createPersistentMessageHandler(e){return t=>{let i=JSON.parse(t.data);if(this.debug("WebSocket persistent message received:",i),_(e)in this.checks||(this.checks[_(e)]={}),(i.flags??[]).forEach(r=>{let s=X(r),l=_(e);this.checks[l]===void 0&&(this.checks[l]={}),this.checks[l][s.flag]=s,typeof s.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(s),((this.flagCheckListeners[r.flag]?.size??0)>0||(this.flagValueListeners[r.flag]?.size??0)>0)&&this.submitFlagCheckEvent(s.flag,s,e),this.debug(`WebSocket flag update received. Notifying listeners for ${r.flag}`,{flag:r.flag,value:s.value,flagCheck:s}),this.notifyFlagCheckListeners(r.flag,s),this.notifyFlagValueListeners(r.flag,s.value)}),i.plan!==void 0&&i.plan!==null){let r=xe(i.plan),s=_(e);this.planChecks[s]=r,this.debug("WebSocket plan update received. Notifying listeners",{plan:r}),this.notifyPlanListeners(r)}this.flushContextDependentEventQueue(),this.setIsPending(!1)}}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(e,t,i){let r={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:",r),this.handleEvent("flag_check",w(r))}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(r=>{if(!r.ok)throw new Error("Network response was not ok");return r.json()}).then(r=>{let s=K(r);return this.debug("checkFlags result:",s),(s?.data?.flags??[]).reduce((l,p)=>(l[p.flag]=p.value,l),{})}).catch(r=>(console.warn("There was a problem with the fetch operation:",r),{}))};identify=e=>(this.debug("identify:",e),this.setContext({company:e.company?.keys,user:e.keys}).catch(t=>{console.warn("Error setting context:",t)}),this.handleEvent("identify",e));setContext=async e=>{if(this.isOffline()||!this.useWebSocket)return this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),Promise.resolve();if(!(_(e)===_(this.context)&&this.conn!==null&&!this.isPending))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){console.warn("Failed to establish WebSocket connection:",t),this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),this.attemptReconnect()}};track=e=>{let{company:t,user:i,event:r,traits:s,quantity:l=1}=e;if(!this.hasContext(t,i)){this.debug(`track: queuing event "${r}" until context is available`);let u={api_key:this.apiKey,body:{company:t,event:r,traits:s??{},user:i,quantity:l},sent_at:new Date().toISOString(),tracker_event_id:O(),tracker_user_id:this.getAnonymousId(),type:"track"};return this.contextDependentEventQueue.push(u),Promise.resolve()}let p={company:t??this.context.company,event:r,traits:s??{},user:i??this.context.user,quantity:l};return this.debug("track:",p),r in this.featureUsageEventMap&&this.optimisticallyUpdateFeatureUsage(r,l),this.handleEvent("track",p)};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(([r,s])=>{if(s===void 0)return;let l={...s};if(typeof l.featureUsage=="number"){if(l.featureUsage+=t,typeof l.featureAllocation=="number"){let u=l.featureUsageExceeded===!0,f=l.featureUsage>=l.featureAllocation;f!==u&&(l.featureUsageExceeded=f,f&&(l.value=!1),this.debug(`Usage limit status changed for flag: ${r}`,{was:u?"exceeded":"within limits",now:f?"exceeded":"within limits",featureUsage:l.featureUsage,featureAllocation:l.featureAllocation,value:l.value}))}this.featureUsageEventMap[e]!==void 0&&(this.featureUsageEventMap[e][r]=l);let p=_(this.context);this.checks[p]!==void 0&&this.checks[p]!==null&&(this.checks[p][r]=l),this.notifyFlagCheckListeners(r,l),this.notifyFlagValueListeners(r,l.value)}}))};hasContext=(e,t)=>{let i=e!=null&&Object.keys(e).length>0||t!=null&&Object.keys(t).length>0,r=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||r};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},r={...e,body:i,sent_at:new Date().toISOString()};this.sendEvent(r)}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 r of this.eventQueue)r.next_retry_at===void 0||r.next_retry_at<=e?t.push(r):i.push(r);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 r of t)try{await this.sendEvent(r),this.debug("Queued event sent successfully:",r.type)}catch(s){this.debug("Failed to send queued event:",s)}};getAnonymousId=()=>{if(!this.storage)return O();let e=this.storage.getItem(Be);if(typeof e<"u")return e;let t=O();return this.storage.setItem(Be,t),t};handleEvent=(e,t)=>{let i={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:O(),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 r=await fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:i});if(!r.ok)throw new Error(`HTTP ${r.status}: ${r.statusText}`);this.debug("event sent:",{status:r.status,statusText:r.statusText})}catch(r){let s=(e.retry_count??0)+1;if(s<=this.maxEventRetries){this.debug(`Event failed to send (attempt ${s}/${this.maxEventRetries}), queueing for retry:`,r);let l=this.eventRetryInitialDelay*Math.pow(2,s-1),p=Math.min(l,this.eventRetryMaxDelay),u=Date.now()+p,f={...e,retry_count:s,next_retry_at:u};this.eventQueue.length<this.maxEventQueueSize?(this.eventQueue.push(f),this.debug(`Event queued for retry in ${p}ms (${this.eventQueue.length}/${this.maxEventQueueSize})`)):(this.debug(`Event queue full (${this.maxEventQueueSize}), dropping oldest event`),this.eventQueue.shift(),this.eventQueue.push(f)),this.startRetryTimer()}else this.debug(`Event failed permanently after ${this.maxEventRetries} attempts, dropping:`,r)}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 r=await this.conn;this.currentWebSocket===r&&(this.currentWebSocket=null),(r.readyState===WebSocket.OPEN||r.readyState===WebSocket.CONNECTING)&&r.close()}catch(r){this.debug(`${i}: error closing existing connection:`,r)}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 r=await this.conn;this.isConnecting=!1,await this.wsSendMessage(r,this.context,!0),this.debug(`${i}: reconnection successful`)}catch(r){this.isConnecting=!1,this.debug(`${i}: reconnection failed:`,r)}}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.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),i=Math.random()*t*.5,r=t+i;return this.debug(`Reconnect delay calculated: ${r.toFixed(0)}ms (attempt ${this.wsReconnectAttempts+1}/${this.webSocketMaxReconnectAttempts})`),r};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=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 i=0;i<t;i++)try{let r=await this.wsConnectOnce();return this.wsReconnectAttempts=0,r}catch(r){if(e=r instanceof Error?r:new Error(String(r)),!(e.message==="WebSocket connection timeout"))throw this.debug("WebSocket connection failed with non-timeout error, not retrying:",e.message),e;if(i<t-1){let l=this.webSocketInitialRetryDelay*Math.pow(2,i),p=Math.min(l,this.webSocketMaxRetryDelay),u=p*.2*Math.random(),f=p+u;this.debug(`WebSocket connection timeout (attempt ${i+1}/${t}), retrying in ${f.toFixed(0)}ms`),await new Promise(D=>setTimeout(D,f))}else this.debug(`WebSocket connection timeout (attempt ${i+1}/${t}), no more retries`)}throw e??new Error("WebSocket connection failed")};wsConnectOnce=()=>new Promise((e,t)=>{let i=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",i);let r=new WebSocket(i),s=Math.random().toString(36).substring(7);this.debug(`Creating WebSocket connection ${s} to ${i}`);let l=null,p=!1;l=setTimeout(()=>{p||(p=!0,this.debug(`WebSocket connection timeout after ${this.webSocketConnectionTimeout}ms`),r.close(),t(new Error("WebSocket connection timeout")))},this.webSocketConnectionTimeout),r.onopen=()=>{p||(p=!0,l!==null&&clearTimeout(l),this.wsIntentionalDisconnect=!1,this.debug(`WebSocket connection ${s} opened successfully`),e(r))},r.onerror=u=>{if(p)return;p=!0,l!==null&&clearTimeout(l),this.debug(`WebSocket connection ${s} error:`,u);let f=new Error("WebSocket connection failed during handshake");t(f)},r.onclose=()=>{l!==null&&clearTimeout(l),this.debug(`WebSocket connection ${s} closed`),this.conn=null,this.currentWebSocket===r&&(this.currentWebSocket=null,this.isConnecting=!1),!p&&!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((r,s)=>{if(!i&&_(t)==_(this.context))return this.debug("WebSocket context unchanged, skipping update"),r(this.setIsPending(!1));this.debug(i?"WebSocket force sending context (reconnection):":"WebSocket context updated:",t),this.context=t;let l=()=>{let p=!1,u=this.createPersistentMessageHandler(t),f=S=>{u(S),p||(p=!0,r())};e.addEventListener("message",f),e.addEventListener("close",S=>{p||(p=!0,S.code===4001?s(new Error(`Authentication failed: ${S.reason!==""?S.reason:"Invalid API key"}`)):s(new Error("WebSocket connection closed unexpectedly")))}),this.currentWebSocket=e;let D=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${Z}`,y={apiKey:this.apiKey,clientVersion:D,data:t};this.debug("WebSocket sending message:",y),e.send(JSON.stringify(y))};e.readyState===WebSocket.OPEN?(this.debug("WebSocket already open, sending message"),l()):e.readyState===WebSocket.CONNECTING?(this.debug("WebSocket connecting, waiting for open to send message"),e.addEventListener("open",l)):(this.debug("WebSocket is closed, cannot send message"),s("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.isPending=e,this.isPendingListeners.forEach(t=>Mt(t,e)))};getPlan=()=>{let e=_(this.context);return this.planChecks[e]};getFlagCheck=e=>{let t=_(this.context),r=(this.checks[t]??{})[e];if(r!==void 0)return r;if(e in this.flagCheckDefaults||e in this.flagValueDefaults)return e in this.fallbackCheckCache||(this.fallbackCheckCache[e]=this.resolveFallbackCheckFlagReturn(e,void 0,"Default value used")),this.fallbackCheckCache[e]};getFlagValue=e=>{let t=_(this.context),r=(this.checks[t]??{})[e];if(r?.value!==void 0)return r.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)});addPlanListener=e=>(this.planListeners.add(e),()=>{this.planListeners.delete(e)});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(r=>qt(r,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((r,s)=>{this.debug(`Calling listener ${s} for flag ${e}`,{flagKey:e,value:t}),Wt(r,t),this.debug(`Listener ${s} for flag ${e} completed`,{flagKey:e,value:t})})};notifyPlanListeners=e=>{let t=this.planListeners??[];t.size>0&&this.debug(`Notifying ${t.size} plan listeners`,{value:e}),t.forEach((i,r)=>{this.debug(`Calling listener ${r} for plan`,{value:e}),$t(i,e),this.debug(`Listener ${r} for plan completed`,{value:e})})}},Mt=(n,e)=>{n.length>0?n(e):n()},qt=(n,e)=>{n.length>0?n(e):n()},Wt=(n,e)=>{n.length>0?n(e):n()},$t=(n,e)=>{n.length>0?n(e):n()};window.Schematic=U;})();
3
3
  /* @preserve */
@@ -572,6 +572,8 @@ __export(index_exports, {
572
572
  CheckFlagResponseFromJSON: () => CheckFlagResponseFromJSON,
573
573
  CheckFlagReturnFromJSON: () => CheckFlagReturnFromJSON,
574
574
  CheckFlagsResponseFromJSON: () => CheckFlagsResponseFromJSON,
575
+ CheckPlanReturnFromJSON: () => CheckPlanReturnFromJSON,
576
+ DatastreamCompanyPlanFromJSON: () => DatastreamCompanyPlanFromJSON,
575
577
  EventBodyFlagCheckToJSON: () => EventBodyFlagCheckToJSON,
576
578
  RuleType: () => RuleType,
577
579
  Schematic: () => Schematic,
@@ -698,28 +700,6 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
698
700
  };
699
701
  }
700
702
 
701
- // src/types/api/models/EventBodyFlagCheck.ts
702
- function EventBodyFlagCheckToJSON(json) {
703
- return EventBodyFlagCheckToJSONTyped(json, false);
704
- }
705
- function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
706
- if (value == null) {
707
- return value;
708
- }
709
- return {
710
- company_id: value["companyId"],
711
- error: value["error"],
712
- flag_id: value["flagId"],
713
- flag_key: value["flagKey"],
714
- reason: value["reason"],
715
- req_company: value["reqCompany"],
716
- req_user: value["reqUser"],
717
- rule_id: value["ruleId"],
718
- user_id: value["userId"],
719
- value: value["value"]
720
- };
721
- }
722
-
723
703
  // src/types/api/models/CheckFlagResponse.ts
724
704
  function CheckFlagResponseFromJSON(json) {
725
705
  return CheckFlagResponseFromJSONTyped(json, false);
@@ -734,6 +714,21 @@ function CheckFlagResponseFromJSONTyped(json, ignoreDiscriminator) {
734
714
  };
735
715
  }
736
716
 
717
+ // src/types/api/models/DatastreamCompanyPlan.ts
718
+ function DatastreamCompanyPlanFromJSON(json) {
719
+ return DatastreamCompanyPlanFromJSONTyped(json, false);
720
+ }
721
+ function DatastreamCompanyPlanFromJSONTyped(json, ignoreDiscriminator) {
722
+ if (json == null) {
723
+ return json;
724
+ }
725
+ return {
726
+ id: json["id"],
727
+ name: json["name"],
728
+ trialEndDate: json["trial_end_date"] == null ? void 0 : new Date(json["trial_end_date"])
729
+ };
730
+ }
731
+
737
732
  // src/types/api/models/CheckFlagsResponseData.ts
738
733
  function CheckFlagsResponseDataFromJSON(json) {
739
734
  return CheckFlagsResponseDataFromJSONTyped(json, false);
@@ -743,7 +738,8 @@ function CheckFlagsResponseDataFromJSONTyped(json, ignoreDiscriminator) {
743
738
  return json;
744
739
  }
745
740
  return {
746
- flags: json["flags"].map(CheckFlagResponseDataFromJSON)
741
+ flags: json["flags"].map(CheckFlagResponseDataFromJSON),
742
+ plan: json["plan"] == null ? void 0 : DatastreamCompanyPlanFromJSON(json["plan"])
747
743
  };
748
744
  }
749
745
 
@@ -761,6 +757,28 @@ function CheckFlagsResponseFromJSONTyped(json, ignoreDiscriminator) {
761
757
  };
762
758
  }
763
759
 
760
+ // src/types/api/models/EventBodyFlagCheck.ts
761
+ function EventBodyFlagCheckToJSON(json) {
762
+ return EventBodyFlagCheckToJSONTyped(json, false);
763
+ }
764
+ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
765
+ if (value == null) {
766
+ return value;
767
+ }
768
+ return {
769
+ company_id: value["companyId"],
770
+ error: value["error"],
771
+ flag_id: value["flagId"],
772
+ flag_key: value["flagKey"],
773
+ reason: value["reason"],
774
+ req_company: value["reqCompany"],
775
+ req_user: value["reqUser"],
776
+ rule_id: value["ruleId"],
777
+ user_id: value["userId"],
778
+ value: value["value"]
779
+ };
780
+ }
781
+
764
782
  // src/types/index.ts
765
783
  var RuleType = /* @__PURE__ */ ((RuleType2) => {
766
784
  RuleType2["GLOBAL_OVERRIDE"] = "global_override";
@@ -817,6 +835,14 @@ var CheckFlagReturnFromJSON = (json) => {
817
835
  value
818
836
  };
819
837
  };
838
+ var CheckPlanReturnFromJSON = (json) => {
839
+ const { id, name, trialEndDate } = DatastreamCompanyPlanFromJSON(json);
840
+ return {
841
+ id,
842
+ name,
843
+ trialEndDate: trialEndDate == null ? void 0 : trialEndDate
844
+ };
845
+ };
820
846
 
821
847
  // src/utils.ts
822
848
  function contextString(context) {
@@ -835,7 +861,7 @@ function contextString(context) {
835
861
  }
836
862
 
837
863
  // src/version.ts
838
- var version = "1.2.21";
864
+ var version = "1.3.0";
839
865
 
840
866
  // src/index.ts
841
867
  var anonymousIdKey = "schematicId";
@@ -854,10 +880,12 @@ var Schematic = class {
854
880
  flagValueListeners = {};
855
881
  isPending = true;
856
882
  isPendingListeners = /* @__PURE__ */ new Set();
883
+ planListeners = /* @__PURE__ */ new Set();
857
884
  storage;
858
885
  useWebSocket = false;
859
886
  checks = {};
860
887
  featureUsageEventMap = {};
888
+ planChecks = {};
861
889
  webSocketUrl = "wss://api.schematichq.com";
862
890
  webSocketConnectionTimeout = 1e4;
863
891
  webSocketReconnect = true;
@@ -1190,28 +1218,32 @@ var Schematic = class {
1190
1218
  this.checks[contextStr] = {};
1191
1219
  }
1192
1220
  this.checks[contextStr][flagCheck.flag] = flagCheck;
1193
- this.debug(`WebSocket flag update:`, {
1194
- flag: flagCheck.flag,
1195
- value: flagCheck.value,
1196
- flagCheck
1197
- });
1198
1221
  if (typeof flagCheck.featureUsageEvent === "string") {
1199
1222
  this.updateFeatureUsageEventMap(flagCheck);
1200
1223
  }
1201
1224
  if ((this.flagCheckListeners[flag.flag]?.size ?? 0) > 0 || (this.flagValueListeners[flag.flag]?.size ?? 0) > 0) {
1202
1225
  this.submitFlagCheckEvent(flagCheck.flag, flagCheck, context);
1203
1226
  }
1204
- this.debug(`About to notify listeners for flag ${flag.flag}`, {
1205
- flag: flag.flag,
1206
- value: flagCheck.value
1207
- });
1227
+ this.debug(
1228
+ `WebSocket flag update received. Notifying listeners for ${flag.flag}`,
1229
+ {
1230
+ flag: flag.flag,
1231
+ value: flagCheck.value,
1232
+ flagCheck
1233
+ }
1234
+ );
1208
1235
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1209
1236
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1210
- this.debug(`Finished notifying listeners for flag ${flag.flag}`, {
1211
- flag: flag.flag,
1212
- value: flagCheck.value
1213
- });
1214
1237
  });
1238
+ if (message.plan !== void 0 && message.plan !== null) {
1239
+ const plan = CheckPlanReturnFromJSON(message.plan);
1240
+ const contextStr = contextString(context);
1241
+ this.planChecks[contextStr] = plan;
1242
+ this.debug(`WebSocket plan update received. Notifying listeners`, {
1243
+ plan
1244
+ });
1245
+ this.notifyPlanListeners(plan);
1246
+ }
1215
1247
  this.flushContextDependentEventQueue();
1216
1248
  this.setIsPending(false);
1217
1249
  };
@@ -2107,6 +2139,11 @@ var Schematic = class {
2107
2139
  (listener) => notifyPendingListener(listener, isPending)
2108
2140
  );
2109
2141
  };
2142
+ getPlan = () => {
2143
+ const contextStr = contextString(this.context);
2144
+ const plan = this.planChecks[contextStr];
2145
+ return plan;
2146
+ };
2110
2147
  // flag checks state
2111
2148
  getFlagCheck = (flagKey) => {
2112
2149
  const contextStr = contextString(this.context);
@@ -2160,6 +2197,12 @@ var Schematic = class {
2160
2197
  this.flagCheckListeners[flagKey].delete(listener);
2161
2198
  };
2162
2199
  };
2200
+ addPlanListener = (listener) => {
2201
+ this.planListeners.add(listener);
2202
+ return () => {
2203
+ this.planListeners.delete(listener);
2204
+ };
2205
+ };
2163
2206
  notifyFlagCheckListeners = (flagKey, check) => {
2164
2207
  const listeners = this.flagCheckListeners?.[flagKey] ?? [];
2165
2208
  if (listeners.size > 0) {
@@ -2208,6 +2251,21 @@ var Schematic = class {
2208
2251
  });
2209
2252
  });
2210
2253
  };
2254
+ notifyPlanListeners = (value) => {
2255
+ const listeners = this.planListeners ?? [];
2256
+ if (listeners.size > 0) {
2257
+ this.debug(`Notifying ${listeners.size} plan listeners`, { value });
2258
+ }
2259
+ listeners.forEach((listener, index) => {
2260
+ this.debug(`Calling listener ${index} for plan`, {
2261
+ value
2262
+ });
2263
+ notifyPlanListener(listener, value);
2264
+ this.debug(`Listener ${index} for plan completed`, {
2265
+ value
2266
+ });
2267
+ });
2268
+ };
2211
2269
  };
2212
2270
  var notifyPendingListener = (listener, value) => {
2213
2271
  if (listener.length > 0) {
@@ -2230,4 +2288,11 @@ var notifyFlagValueListener = (listener, value) => {
2230
2288
  listener();
2231
2289
  }
2232
2290
  };
2291
+ var notifyPlanListener = (listener, value) => {
2292
+ if (listener.length > 0) {
2293
+ listener(value);
2294
+ } else {
2295
+ listener();
2296
+ }
2297
+ };
2233
2298
  /* @preserve */
@@ -194,6 +194,12 @@ declare interface CheckFlagsResponseData {
194
194
  * @memberof CheckFlagsResponseData
195
195
  */
196
196
  flags: Array<CheckFlagResponseData>;
197
+ /**
198
+ *
199
+ * @type {DatastreamCompanyPlan}
200
+ * @memberof CheckFlagsResponseData
201
+ */
202
+ plan?: DatastreamCompanyPlan;
197
203
  }
198
204
 
199
205
  export declare function CheckFlagsResponseFromJSON(json: any): CheckFlagsResponse;
@@ -204,6 +210,55 @@ export declare type CheckOptions = {
204
210
  key: string;
205
211
  };
206
212
 
213
+ export declare type CheckPlanReturn = {
214
+ id: string;
215
+ name: string;
216
+ trialEndDate?: Date;
217
+ };
218
+
219
+ export declare const CheckPlanReturnFromJSON: (json: any) => CheckPlanReturn;
220
+
221
+ export declare type CheckPlanReturnListenerFn = (value: CheckPlanReturn) => void;
222
+
223
+ /**
224
+ * Schematic API
225
+ * Schematic API
226
+ *
227
+ * The version of the OpenAPI document: 0.1
228
+ *
229
+ *
230
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
231
+ * https://openapi-generator.tech
232
+ * Do not edit the class manually.
233
+ */
234
+ /**
235
+ *
236
+ * @export
237
+ * @interface DatastreamCompanyPlan
238
+ */
239
+ declare interface DatastreamCompanyPlan {
240
+ /**
241
+ *
242
+ * @type {string}
243
+ * @memberof DatastreamCompanyPlan
244
+ */
245
+ id: string;
246
+ /**
247
+ *
248
+ * @type {string}
249
+ * @memberof DatastreamCompanyPlan
250
+ */
251
+ name: string;
252
+ /**
253
+ *
254
+ * @type {Date}
255
+ * @memberof DatastreamCompanyPlan
256
+ */
257
+ trialEndDate?: Date | null;
258
+ }
259
+
260
+ export declare function DatastreamCompanyPlanFromJSON(json: any): DatastreamCompanyPlan;
261
+
207
262
  export declare type EmptyListenerFn = () => void;
208
263
 
209
264
  /**
@@ -462,6 +517,8 @@ export declare type Keys = Record<string, string>;
462
517
 
463
518
  export declare type PendingListenerFn = BooleanListenerFn | EmptyListenerFn;
464
519
 
520
+ export declare type PlanListenerFn = CheckPlanReturnListenerFn | EmptyListenerFn;
521
+
465
522
  export declare enum RuleType {
466
523
  /** A global rule that, if present, will override all other rules for a flag */
467
524
  GLOBAL_OVERRIDE = "global_override",
@@ -494,10 +551,12 @@ export declare class Schematic {
494
551
  private flagValueListeners;
495
552
  private isPending;
496
553
  private isPendingListeners;
554
+ private planListeners;
497
555
  private storage;
498
556
  private useWebSocket;
499
557
  private checks;
500
558
  private featureUsageEventMap;
559
+ private planChecks;
501
560
  private webSocketUrl;
502
561
  private webSocketConnectionTimeout;
503
562
  private webSocketReconnect;
@@ -702,16 +761,19 @@ export declare class Schematic {
702
761
  getIsPending: () => boolean;
703
762
  addIsPendingListener: (listener: PendingListenerFn) => () => void;
704
763
  private setIsPending;
764
+ getPlan: () => CheckPlanReturn | undefined;
705
765
  getFlagCheck: (flagKey: string) => CheckFlagReturn | undefined;
706
766
  getFlagValue: (flagKey: string) => boolean | undefined;
707
767
  /** Register an event listener that will be notified with the boolean value for a given flag when this value changes */
708
768
  addFlagValueListener: (flagKey: string, listener: FlagValueListenerFn) => () => void;
709
769
  /** Register an event listener that will be notified with the full flag check response for a given flag whenever this value changes */
710
770
  addFlagCheckListener: (flagKey: string, listener: FlagCheckListenerFn) => () => void;
771
+ addPlanListener: (listener: PlanListenerFn) => () => void;
711
772
  private notifyFlagCheckListeners;
712
773
  /** Add or update a CheckFlagReturn in the featureUsageEventMap */
713
774
  private updateFeatureUsageEventMap;
714
775
  private notifyFlagValueListeners;
776
+ private notifyPlanListeners;
715
777
  }
716
778
 
717
779
  /** Context for checking flags and sending events */
@@ -679,28 +679,6 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
679
679
  };
680
680
  }
681
681
 
682
- // src/types/api/models/EventBodyFlagCheck.ts
683
- function EventBodyFlagCheckToJSON(json) {
684
- return EventBodyFlagCheckToJSONTyped(json, false);
685
- }
686
- function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
687
- if (value == null) {
688
- return value;
689
- }
690
- return {
691
- company_id: value["companyId"],
692
- error: value["error"],
693
- flag_id: value["flagId"],
694
- flag_key: value["flagKey"],
695
- reason: value["reason"],
696
- req_company: value["reqCompany"],
697
- req_user: value["reqUser"],
698
- rule_id: value["ruleId"],
699
- user_id: value["userId"],
700
- value: value["value"]
701
- };
702
- }
703
-
704
682
  // src/types/api/models/CheckFlagResponse.ts
705
683
  function CheckFlagResponseFromJSON(json) {
706
684
  return CheckFlagResponseFromJSONTyped(json, false);
@@ -715,6 +693,21 @@ function CheckFlagResponseFromJSONTyped(json, ignoreDiscriminator) {
715
693
  };
716
694
  }
717
695
 
696
+ // src/types/api/models/DatastreamCompanyPlan.ts
697
+ function DatastreamCompanyPlanFromJSON(json) {
698
+ return DatastreamCompanyPlanFromJSONTyped(json, false);
699
+ }
700
+ function DatastreamCompanyPlanFromJSONTyped(json, ignoreDiscriminator) {
701
+ if (json == null) {
702
+ return json;
703
+ }
704
+ return {
705
+ id: json["id"],
706
+ name: json["name"],
707
+ trialEndDate: json["trial_end_date"] == null ? void 0 : new Date(json["trial_end_date"])
708
+ };
709
+ }
710
+
718
711
  // src/types/api/models/CheckFlagsResponseData.ts
719
712
  function CheckFlagsResponseDataFromJSON(json) {
720
713
  return CheckFlagsResponseDataFromJSONTyped(json, false);
@@ -724,7 +717,8 @@ function CheckFlagsResponseDataFromJSONTyped(json, ignoreDiscriminator) {
724
717
  return json;
725
718
  }
726
719
  return {
727
- flags: json["flags"].map(CheckFlagResponseDataFromJSON)
720
+ flags: json["flags"].map(CheckFlagResponseDataFromJSON),
721
+ plan: json["plan"] == null ? void 0 : DatastreamCompanyPlanFromJSON(json["plan"])
728
722
  };
729
723
  }
730
724
 
@@ -742,6 +736,28 @@ function CheckFlagsResponseFromJSONTyped(json, ignoreDiscriminator) {
742
736
  };
743
737
  }
744
738
 
739
+ // src/types/api/models/EventBodyFlagCheck.ts
740
+ function EventBodyFlagCheckToJSON(json) {
741
+ return EventBodyFlagCheckToJSONTyped(json, false);
742
+ }
743
+ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
744
+ if (value == null) {
745
+ return value;
746
+ }
747
+ return {
748
+ company_id: value["companyId"],
749
+ error: value["error"],
750
+ flag_id: value["flagId"],
751
+ flag_key: value["flagKey"],
752
+ reason: value["reason"],
753
+ req_company: value["reqCompany"],
754
+ req_user: value["reqUser"],
755
+ rule_id: value["ruleId"],
756
+ user_id: value["userId"],
757
+ value: value["value"]
758
+ };
759
+ }
760
+
745
761
  // src/types/index.ts
746
762
  var RuleType = /* @__PURE__ */ ((RuleType2) => {
747
763
  RuleType2["GLOBAL_OVERRIDE"] = "global_override";
@@ -798,6 +814,14 @@ var CheckFlagReturnFromJSON = (json) => {
798
814
  value
799
815
  };
800
816
  };
817
+ var CheckPlanReturnFromJSON = (json) => {
818
+ const { id, name, trialEndDate } = DatastreamCompanyPlanFromJSON(json);
819
+ return {
820
+ id,
821
+ name,
822
+ trialEndDate: trialEndDate == null ? void 0 : trialEndDate
823
+ };
824
+ };
801
825
 
802
826
  // src/utils.ts
803
827
  function contextString(context) {
@@ -816,7 +840,7 @@ function contextString(context) {
816
840
  }
817
841
 
818
842
  // src/version.ts
819
- var version = "1.2.21";
843
+ var version = "1.3.0";
820
844
 
821
845
  // src/index.ts
822
846
  var anonymousIdKey = "schematicId";
@@ -835,10 +859,12 @@ var Schematic = class {
835
859
  flagValueListeners = {};
836
860
  isPending = true;
837
861
  isPendingListeners = /* @__PURE__ */ new Set();
862
+ planListeners = /* @__PURE__ */ new Set();
838
863
  storage;
839
864
  useWebSocket = false;
840
865
  checks = {};
841
866
  featureUsageEventMap = {};
867
+ planChecks = {};
842
868
  webSocketUrl = "wss://api.schematichq.com";
843
869
  webSocketConnectionTimeout = 1e4;
844
870
  webSocketReconnect = true;
@@ -1171,28 +1197,32 @@ var Schematic = class {
1171
1197
  this.checks[contextStr] = {};
1172
1198
  }
1173
1199
  this.checks[contextStr][flagCheck.flag] = flagCheck;
1174
- this.debug(`WebSocket flag update:`, {
1175
- flag: flagCheck.flag,
1176
- value: flagCheck.value,
1177
- flagCheck
1178
- });
1179
1200
  if (typeof flagCheck.featureUsageEvent === "string") {
1180
1201
  this.updateFeatureUsageEventMap(flagCheck);
1181
1202
  }
1182
1203
  if ((this.flagCheckListeners[flag.flag]?.size ?? 0) > 0 || (this.flagValueListeners[flag.flag]?.size ?? 0) > 0) {
1183
1204
  this.submitFlagCheckEvent(flagCheck.flag, flagCheck, context);
1184
1205
  }
1185
- this.debug(`About to notify listeners for flag ${flag.flag}`, {
1186
- flag: flag.flag,
1187
- value: flagCheck.value
1188
- });
1206
+ this.debug(
1207
+ `WebSocket flag update received. Notifying listeners for ${flag.flag}`,
1208
+ {
1209
+ flag: flag.flag,
1210
+ value: flagCheck.value,
1211
+ flagCheck
1212
+ }
1213
+ );
1189
1214
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1190
1215
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1191
- this.debug(`Finished notifying listeners for flag ${flag.flag}`, {
1192
- flag: flag.flag,
1193
- value: flagCheck.value
1194
- });
1195
1216
  });
1217
+ if (message.plan !== void 0 && message.plan !== null) {
1218
+ const plan = CheckPlanReturnFromJSON(message.plan);
1219
+ const contextStr = contextString(context);
1220
+ this.planChecks[contextStr] = plan;
1221
+ this.debug(`WebSocket plan update received. Notifying listeners`, {
1222
+ plan
1223
+ });
1224
+ this.notifyPlanListeners(plan);
1225
+ }
1196
1226
  this.flushContextDependentEventQueue();
1197
1227
  this.setIsPending(false);
1198
1228
  };
@@ -2088,6 +2118,11 @@ var Schematic = class {
2088
2118
  (listener) => notifyPendingListener(listener, isPending)
2089
2119
  );
2090
2120
  };
2121
+ getPlan = () => {
2122
+ const contextStr = contextString(this.context);
2123
+ const plan = this.planChecks[contextStr];
2124
+ return plan;
2125
+ };
2091
2126
  // flag checks state
2092
2127
  getFlagCheck = (flagKey) => {
2093
2128
  const contextStr = contextString(this.context);
@@ -2141,6 +2176,12 @@ var Schematic = class {
2141
2176
  this.flagCheckListeners[flagKey].delete(listener);
2142
2177
  };
2143
2178
  };
2179
+ addPlanListener = (listener) => {
2180
+ this.planListeners.add(listener);
2181
+ return () => {
2182
+ this.planListeners.delete(listener);
2183
+ };
2184
+ };
2144
2185
  notifyFlagCheckListeners = (flagKey, check) => {
2145
2186
  const listeners = this.flagCheckListeners?.[flagKey] ?? [];
2146
2187
  if (listeners.size > 0) {
@@ -2189,6 +2230,21 @@ var Schematic = class {
2189
2230
  });
2190
2231
  });
2191
2232
  };
2233
+ notifyPlanListeners = (value) => {
2234
+ const listeners = this.planListeners ?? [];
2235
+ if (listeners.size > 0) {
2236
+ this.debug(`Notifying ${listeners.size} plan listeners`, { value });
2237
+ }
2238
+ listeners.forEach((listener, index) => {
2239
+ this.debug(`Calling listener ${index} for plan`, {
2240
+ value
2241
+ });
2242
+ notifyPlanListener(listener, value);
2243
+ this.debug(`Listener ${index} for plan completed`, {
2244
+ value
2245
+ });
2246
+ });
2247
+ };
2192
2248
  };
2193
2249
  var notifyPendingListener = (listener, value) => {
2194
2250
  if (listener.length > 0) {
@@ -2211,10 +2267,19 @@ var notifyFlagValueListener = (listener, value) => {
2211
2267
  listener();
2212
2268
  }
2213
2269
  };
2270
+ var notifyPlanListener = (listener, value) => {
2271
+ if (listener.length > 0) {
2272
+ listener(value);
2273
+ } else {
2274
+ listener();
2275
+ }
2276
+ };
2214
2277
  export {
2215
2278
  CheckFlagResponseFromJSON,
2216
2279
  CheckFlagReturnFromJSON,
2217
2280
  CheckFlagsResponseFromJSON,
2281
+ CheckPlanReturnFromJSON,
2282
+ DatastreamCompanyPlanFromJSON,
2218
2283
  EventBodyFlagCheckToJSON,
2219
2284
  RuleType,
2220
2285
  Schematic,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematichq/schematic-js",
3
- "version": "1.2.21",
3
+ "version": "1.3.0",
4
4
  "main": "dist/schematic.cjs.js",
5
5
  "module": "dist/schematic.esm.js",
6
6
  "types": "dist/schematic.d.ts",
@@ -25,7 +25,7 @@
25
25
  "clean": "rm -rf dist",
26
26
  "format": "prettier --write src/*.ts",
27
27
  "lint": "eslint src --report-unused-disable-directives --fix",
28
- "openapi": "rm -rf src/types/api/ && npx openapi-generator-cli generate -c openapi-config.yaml --global-property models=\"EventBody:EventBodyFlagCheck:EventBodyIdentify:EventBodyIdentifyCompany:EventBodyTrack:CheckFlagResponse:CheckFlagResponseData:CheckFlagsResponse:CheckFlagsResponseData:FeatureEntitlement:EntitlementValueType\",supportingFiles=runtime.ts && prettier --write \"src/types/api/**/*.{ts,tsx}\"",
28
+ "openapi": "rm -rf src/types/api/ && npx openapi-generator-cli generate -c openapi-config.yaml && prettier --write \"src/types/api/**/*.{ts,tsx}\"",
29
29
  "prepare": "husky",
30
30
  "test": "vitest run",
31
31
  "test:reactnative": "vitest run --config vitest.config.reactnative.ts",
@@ -38,20 +38,25 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@eslint/js": "^10.0.1",
41
- "@microsoft/api-extractor": "^7.57.6",
42
- "@openapitools/openapi-generator-cli": "^2.30.0",
41
+ "@microsoft/api-extractor": "^7.57.7",
42
+ "@openapitools/openapi-generator-cli": "^2.30.2",
43
43
  "@vitest/browser": "^4.0.18",
44
- "esbuild": "^0.27.3",
45
- "eslint": "^10.0.2",
44
+ "esbuild": "^0.27.4",
45
+ "eslint": "^10.0.3",
46
46
  "globals": "^17.4.0",
47
- "happy-dom": "^20.7.0",
47
+ "happy-dom": "^20.8.4",
48
48
  "husky": "^9.1.7",
49
- "jsdom": "^28.1.0",
49
+ "jsdom": "^29.0.0",
50
50
  "mock-socket": "^9.3.1",
51
51
  "prettier": "^3.8.1",
52
52
  "typescript": "^5.9.3",
53
- "typescript-eslint": "^8.56.1",
53
+ "typescript-eslint": "^8.57.0",
54
54
  "vitest": "^4.0.18"
55
55
  },
56
+ "resolutions": {
57
+ "basic-ftp": ">=5.2.0",
58
+ "rollup": ">=4.59.0",
59
+ "undici": ">=7.24.0"
60
+ },
56
61
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
57
62
  }