@schematichq/schematic-js 1.2.1 → 1.2.2

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.
package/README.md CHANGED
@@ -82,6 +82,36 @@ await schematic.checkFlag("some-flag-key");
82
82
  schematic.cleanup();
83
83
  ```
84
84
 
85
+ ## Troubleshooting
86
+
87
+ For debugging and development, Schematic supports two special modes:
88
+
89
+ ### Debug Mode
90
+
91
+ Enables console logging of all Schematic operations:
92
+
93
+ ```typescript
94
+ // Enable at initialization
95
+ const schematic = new Schematic("your-api-key", { debug: true });
96
+
97
+ // Or via URL parameter
98
+ // https://yoursite.com/?schematic_debug=true
99
+ ```
100
+
101
+ ### Offline Mode
102
+
103
+ Prevents network requests and returns fallback values for all flag checks:
104
+
105
+ ```typescript
106
+ // Enable at initialization
107
+ const schematic = new Schematic("your-api-key", { offline: true });
108
+
109
+ // Or via URL parameter
110
+ // https://yoursite.com/?schematic_offline=true
111
+ ```
112
+
113
+ Offline mode automatically enables debug mode to help with troubleshooting.
114
+
85
115
  ## License
86
116
 
87
117
  MIT
@@ -1,3 +1,3 @@
1
- "use strict";(()=>{var re=Object.create;var M=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ae=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var ie=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports);var le=(r,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of se(t))!oe.call(r,a)&&a!==n&&M(r,a,{get:()=>t[a],enumerable:!(o=ne(t,a))||o.enumerable});return r};var ce=(r,t,n)=>(n=r!=null?re(ae(r)):{},le(t||!r||!r.__esModule?M(n,"default",{value:r,enumerable:!0}):n,r));var W=ie(K=>{(function(r){var t=function(n){var o=typeof globalThis<"u"&&globalThis||typeof r<"u"&&r||typeof global<"u"&&global||{},a={searchParams:"URLSearchParams"in o,iterable:"Symbol"in o&&"iterator"in Symbol,blob:"FileReader"in o&&"Blob"in o&&function(){try{return new Blob,!0}catch{return!1}}(),formData:"FormData"in o,arrayBuffer:"ArrayBuffer"in o};function h(e){return e&&DataView.prototype.isPrototypeOf(e)}if(a.arrayBuffer)var d=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],p=ArrayBuffer.isView||function(e){return e&&d.indexOf(Object.prototype.toString.call(e))>-1};function m(e){if(typeof e!="string"&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(e)||e==="")throw new TypeError('Invalid character in header field name: "'+e+'"');return e.toLowerCase()}function E(e){return typeof e!="string"&&(e=String(e)),e}function k(e){var s={next:function(){var i=e.shift();return{done:i===void 0,value:i}}};return a.iterable&&(s[Symbol.iterator]=function(){return s}),s}function c(e){this.map={},e instanceof c?e.forEach(function(s,i){this.append(i,s)},this):Array.isArray(e)?e.forEach(function(s){if(s.length!=2)throw new TypeError("Headers constructor: expected name/value pair to be length 2, found"+s.length);this.append(s[0],s[1])},this):e&&Object.getOwnPropertyNames(e).forEach(function(s){this.append(s,e[s])},this)}c.prototype.append=function(e,s){e=m(e),s=E(s);var i=this.map[e];this.map[e]=i?i+", "+s:s},c.prototype.delete=function(e){delete this.map[m(e)]},c.prototype.get=function(e){return e=m(e),this.has(e)?this.map[e]:null},c.prototype.has=function(e){return this.map.hasOwnProperty(m(e))},c.prototype.set=function(e,s){this.map[m(e)]=E(s)},c.prototype.forEach=function(e,s){for(var i in this.map)this.map.hasOwnProperty(i)&&e.call(s,this.map[i],i,this)},c.prototype.keys=function(){var e=[];return this.forEach(function(s,i){e.push(i)}),k(e)},c.prototype.values=function(){var e=[];return this.forEach(function(s){e.push(s)}),k(e)},c.prototype.entries=function(){var e=[];return this.forEach(function(s,i){e.push([i,s])}),k(e)},a.iterable&&(c.prototype[Symbol.iterator]=c.prototype.entries);function R(e){if(!e._noBody){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}}function x(e){return new Promise(function(s,i){e.onload=function(){s(e.result)},e.onerror=function(){i(e.error)}})}function B(e){var s=new FileReader,i=x(s);return s.readAsArrayBuffer(e),i}function Q(e){var s=new FileReader,i=x(s),u=/charset=([A-Za-z0-9_-]+)/.exec(e.type),f=u?u[1]:"utf-8";return s.readAsText(e,f),i}function j(e){for(var s=new Uint8Array(e),i=new Array(s.length),u=0;u<s.length;u++)i[u]=String.fromCharCode(s[u]);return i.join("")}function J(e){if(e.slice)return e.slice(0);var s=new Uint8Array(e.byteLength);return s.set(new Uint8Array(e)),s.buffer}function V(){return this.bodyUsed=!1,this._initBody=function(e){this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?typeof e=="string"?this._bodyText=e:a.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:a.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:a.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():a.arrayBuffer&&a.blob&&h(e)?(this._bodyArrayBuffer=J(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):a.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||p(e))?this._bodyArrayBuffer=J(e):this._bodyText=e=Object.prototype.toString.call(e):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof e=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):a.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},a.blob&&(this.blob=function(){var e=R(this);if(e)return e;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 e=R(this);return e||(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(a.blob)return this.blob().then(B);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var e=R(this);if(e)return e;if(this._bodyBlob)return Q(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)},a.formData&&(this.formData=function(){return this.text().then(Z)}),this.json=function(){return this.text().then(JSON.parse)},this}var z=["CONNECT","DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT","TRACE"];function Y(e){var s=e.toUpperCase();return z.indexOf(s)>-1?s:e}function F(e,s){if(!(this instanceof F))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');s=s||{};var i=s.body;if(e instanceof F){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,s.headers||(this.headers=new c(e.headers)),this.method=e.method,this.mode=e.mode,this.signal=e.signal,!i&&e._bodyInit!=null&&(i=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=s.credentials||this.credentials||"same-origin",(s.headers||!this.headers)&&(this.headers=new c(s.headers)),this.method=Y(s.method||this.method||"GET"),this.mode=s.mode||this.mode||null,this.signal=s.signal||this.signal||function(){if("AbortController"in o){var l=new AbortController;return l.signal}}(),this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&i)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(i),(this.method==="GET"||this.method==="HEAD")&&(s.cache==="no-store"||s.cache==="no-cache")){var u=/([?&])_=[^&]*/;if(u.test(this.url))this.url=this.url.replace(u,"$1_="+new Date().getTime());else{var f=/\?/;this.url+=(f.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}F.prototype.clone=function(){return new F(this,{body:this._bodyInit})};function Z(e){var s=new FormData;return e.trim().split("&").forEach(function(i){if(i){var u=i.split("="),f=u.shift().replace(/\+/g," "),l=u.join("=").replace(/\+/g," ");s.append(decodeURIComponent(f),decodeURIComponent(l))}}),s}function ee(e){var s=new c,i=e.replace(/\r?\n[\t ]+/g," ");return i.split("\r").map(function(u){return u.indexOf(`
2
- `)===0?u.substr(1,u.length):u}).forEach(function(u){var f=u.split(":"),l=f.shift().trim();if(l){var T=f.join(":").trim();try{s.append(l,T)}catch(A){console.warn("Response "+A.message)}}}),s}V.call(F.prototype);function b(e,s){if(!(this instanceof b))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(s||(s={}),this.type="default",this.status=s.status===void 0?200:s.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=s.statusText===void 0?"":""+s.statusText,this.headers=new c(s.headers),this.url=s.url||"",this._initBody(e)}V.call(b.prototype),b.prototype.clone=function(){return new b(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new c(this.headers),url:this.url})},b.error=function(){var e=new b(null,{status:200,statusText:""});return e.ok=!1,e.status=0,e.type="error",e};var te=[301,302,303,307,308];b.redirect=function(e,s){if(te.indexOf(s)===-1)throw new RangeError("Invalid status code");return new b(null,{status:s,headers:{location:e}})},n.DOMException=o.DOMException;try{new n.DOMException}catch{n.DOMException=function(s,i){this.message=s,this.name=i;var u=Error(s);this.stack=u.stack},n.DOMException.prototype=Object.create(Error.prototype),n.DOMException.prototype.constructor=n.DOMException}function O(e,s){return new Promise(function(i,u){var f=new F(e,s);if(f.signal&&f.signal.aborted)return u(new n.DOMException("Aborted","AbortError"));var l=new XMLHttpRequest;function T(){l.abort()}l.onload=function(){var y={statusText:l.statusText,headers:ee(l.getAllResponseHeaders()||"")};f.url.indexOf("file://")===0&&(l.status<200||l.status>599)?y.status=200:y.status=l.status,y.url="responseURL"in l?l.responseURL:y.headers.get("X-Request-URL");var C="response"in l?l.response:l.responseText;setTimeout(function(){i(new b(C,y))},0)},l.onerror=function(){setTimeout(function(){u(new TypeError("Network request failed"))},0)},l.ontimeout=function(){setTimeout(function(){u(new TypeError("Network request timed out"))},0)},l.onabort=function(){setTimeout(function(){u(new n.DOMException("Aborted","AbortError"))},0)};function A(y){try{return y===""&&o.location.href?o.location.href:y}catch{return y}}if(l.open(f.method,A(f.url),!0),f.credentials==="include"?l.withCredentials=!0:f.credentials==="omit"&&(l.withCredentials=!1),"responseType"in l&&(a.blob?l.responseType="blob":a.arrayBuffer&&(l.responseType="arraybuffer")),s&&typeof s.headers=="object"&&!(s.headers instanceof c||o.Headers&&s.headers instanceof o.Headers)){var H=[];Object.getOwnPropertyNames(s.headers).forEach(function(y){H.push(m(y)),l.setRequestHeader(y,E(s.headers[y]))}),f.headers.forEach(function(y,C){H.indexOf(C)===-1&&l.setRequestHeader(C,y)})}else f.headers.forEach(function(y,C){l.setRequestHeader(C,y)});f.signal&&(f.signal.addEventListener("abort",T),l.onreadystatechange=function(){l.readyState===4&&f.signal.removeEventListener("abort",T)}),l.send(typeof f._bodyInit>"u"?null:f._bodyInit)})}return O.polyfill=!0,o.fetch||(o.fetch=O,o.Headers=c,o.Request=F,o.Response=b),n.Headers=c,n.Request=F,n.Response=b,n.fetch=O,n}({})})(typeof self<"u"?self:K)});var g=[];for(let r=0;r<256;++r)g.push((r+256).toString(16).slice(1));function q(r,t=0){return(g[r[t+0]]+g[r[t+1]]+g[r[t+2]]+g[r[t+3]]+"-"+g[r[t+4]]+g[r[t+5]]+"-"+g[r[t+6]]+g[r[t+7]]+"-"+g[r[t+8]]+g[r[t+9]]+"-"+g[r[t+10]]+g[r[t+11]]+g[r[t+12]]+g[r[t+13]]+g[r[t+14]]+g[r[t+15]]).toLowerCase()}var U,ue=new Uint8Array(16);function L(){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(ue)}var fe=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),P={randomUUID:fe};function de(r,t,n){if(P.randomUUID&&!t&&!r)return P.randomUUID();r=r||{};let o=r.random??r.rng?.()??L();if(o.length<16)throw new Error("Random bytes length must be >= 16");if(o[6]=o[6]&15|64,o[8]=o[8]&63|128,t){if(n=n||0,n<0||n+16>t.length)throw new RangeError(`UUID byte range ${n}:${n+15} is out of buffer bounds`);for(let a=0;a<16;++a)t[n+a]=o[a];return t}return q(o)}var w=de;var Ye=ce(W());function S(r){return pe(r,!1)}function pe(r,t){return r==null?r:{companyId:r.company_id==null?void 0:r.company_id,error:r.error==null?void 0:r.error,featureAllocation:r.feature_allocation==null?void 0:r.feature_allocation,featureUsage:r.feature_usage==null?void 0:r.feature_usage,featureUsagePeriod:r.feature_usage_period==null?void 0:r.feature_usage_period,featureUsageResetAt:r.feature_usage_reset_at==null?void 0:new Date(r.feature_usage_reset_at),flag:r.flag,flagId:r.flag_id==null?void 0:r.flag_id,reason:r.reason,ruleId:r.rule_id==null?void 0:r.rule_id,ruleType:r.rule_type==null?void 0:r.rule_type,userId:r.user_id==null?void 0:r.user_id,value:r.value}}function _(r){return ge(r,!1)}function ge(r,t){return r==null?r:{data:S(r.data),params:r.params}}function $(r){return me(r,!1)}function me(r,t){return r==null?r:{flags:r.flags.map(S)}}function I(r){return be(r,!1)}function be(r,t){return r==null?r:{data:$(r.data),params:r.params}}var X=r=>{let{companyId:t,error:n,featureAllocation:o,featureUsage:a,featureUsagePeriod:h,featureUsageResetAt:d,flag:p,flagId:m,reason:E,ruleId:k,ruleType:c,userId:R,value:x}=S(r);return{featureUsageExceeded:!x&&(c=="company_override_usage_exceeded"||c=="plan_entitlement_usage_exceeded"),companyId:t??void 0,error:n??void 0,featureAllocation:o??void 0,featureUsage:a??void 0,featureUsagePeriod:h??void 0,featureUsageResetAt:d??void 0,flag:p,flagId:m??void 0,reason:E,ruleId:k??void 0,ruleType:c??void 0,userId:R??void 0,value:x}};function v(r){let t=Object.keys(r).reduce((n,o)=>{let h=Object.keys(r[o]||{}).sort().reduce((d,p)=>(d[p]=r[o][p],d),{});return n[o]=h,n},{});return JSON.stringify(t)}var N="1.2.1";var G="schematicId";var D=class{additionalHeaders={};apiKey;apiUrl="https://api.schematichq.com";conn=null;context={};eventQueue;eventUrl="https://c.schematichq.com";flagCheckListeners={};flagValueListeners={};isPending=!0;isPendingListeners=new Set;storage;useWebSocket=!1;checks={};webSocketUrl="wss://api.schematichq.com";constructor(t,n){this.apiKey=t,this.eventQueue=[],this.useWebSocket=n?.useWebSocket??!1,this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${N}`,...n?.additionalHeaders??{}},n?.storage?this.storage=n.storage:typeof localStorage<"u"&&(this.storage=localStorage),n?.apiUrl!==void 0&&(this.apiUrl=n.apiUrl),n?.eventUrl!==void 0&&(this.eventUrl=n.eventUrl),n?.webSocketUrl!==void 0&&(this.webSocketUrl=n.webSocketUrl),typeof window<"u"&&window?.addEventListener&&window.addEventListener("beforeunload",()=>{this.flushEventQueue()})}async checkFlag(t){let{fallback:n=!1,key:o}=t,a=t.context||this.context,h=v(a);if(!this.useWebSocket){let d=`${this.apiUrl}/flags/${o}/check`;return fetch(d,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(a)}).then(p=>{if(!p.ok)throw new Error("Network response was not ok");return p.json()}).then(p=>_(p).data.value).catch(p=>(console.error("There was a problem with the fetch operation:",p),n))}try{let d=this.checks[h];if(this.conn&&typeof d<"u"&&typeof d[o]<"u")return d[o].value;try{await this.setContext(a)}catch(m){return console.error("WebSocket connection failed, falling back to REST:",m),this.fallbackToRest(o,a,n)}return(this.checks[h]??{})[o]?.value??n}catch(d){return console.error("Unexpected error in checkFlag:",d),n}}async fallbackToRest(t,n,o){try{let a=`${this.apiUrl}/flags/${t}/check`,h=await fetch(a,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(n)});if(!h.ok)throw new Error("Network response was not ok");return _(await h.json())?.data?.value??!1}catch(a){return console.error("REST API call failed, using fallback value:",a),o}}checkFlags=async t=>{t=t||this.context;let n=`${this.apiUrl}/flags/check`,o=JSON.stringify(t);return fetch(n,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:o}).then(a=>{if(!a.ok)throw new Error("Network response was not ok");return a.json()}).then(a=>(I(a)?.data?.flags??[]).reduce((d,p)=>(d[p.flag]=p.value,d),{})).catch(a=>(console.error("There was a problem with the fetch operation:",a),{}))};identify=t=>{try{this.setContext({company:t.company?.keys,user:t.keys})}catch(n){console.error("Error setting context:",n)}return this.handleEvent("identify",t)};setContext=async t=>{if(!this.useWebSocket)return this.context=t,Promise.resolve();try{this.setIsPending(!0),this.conn||(this.conn=this.wsConnect());let n=await this.conn;await this.wsSendMessage(n,t)}catch(n){throw console.error("Failed to establish WebSocket connection:",n),n}};track=t=>{let{company:n,user:o,event:a,traits:h}=t;return this.handleEvent("track",{company:n??this.context.company,event:a,traits:h??{},user:o??this.context.user})};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let t=this.eventQueue.shift();t&&this.sendEvent(t)}};getAnonymousId=()=>{if(!this.storage)return w();let t=this.storage.getItem(G);if(typeof t<"u")return t;let n=w();return this.storage.setItem(G,n),n};handleEvent=(t,n)=>{let o={api_key:this.apiKey,body:n,sent_at:new Date().toISOString(),tracker_event_id:w(),tracker_user_id:this.getAnonymousId(),type:t};return document?.hidden?this.storeEvent(o):this.sendEvent(o)};sendEvent=async t=>{let n=`${this.eventUrl}/e`,o=JSON.stringify(t);try{await fetch(n,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:o})}catch(a){console.error("Error sending Schematic event: ",a)}return Promise.resolve()};storeEvent=t=>(this.eventQueue.push(t),Promise.resolve());cleanup=async()=>{if(this.conn)try{(await this.conn).close()}catch(t){console.error("Error during cleanup:",t)}finally{this.conn=null}};wsConnect=()=>new Promise((t,n)=>{let o=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`,a=new WebSocket(o);a.onopen=()=>{t(a)},a.onerror=h=>{n(h)},a.onclose=()=>{this.conn=null}});wsSendMessage=(t,n)=>new Promise((o,a)=>{if(v(n)==v(this.context))return o(this.setIsPending(!1));this.context=n;let h=()=>{let d=!1,p=m=>{let E=JSON.parse(m.data);v(n)in this.checks||(this.checks[v(n)]={}),(E.flags??[]).forEach(k=>{let c=X(k);this.checks[v(n)][c.flag]=c,this.notifyFlagCheckListeners(k.flag,c),this.notifyFlagValueListeners(k.flag,c.value)}),this.setIsPending(!1),d||(d=!0,o())};t.addEventListener("message",p),t.send(JSON.stringify({apiKey:this.apiKey,clientVersion:`schematic-js@${N}`,data:n}))};t.readyState===WebSocket.OPEN?h():t.readyState===WebSocket.CONNECTING?t.addEventListener("open",h):a("WebSocket is not open or connecting")});getIsPending=()=>this.isPending;addIsPendingListener=t=>(this.isPendingListeners.add(t),()=>{this.isPendingListeners.delete(t)});setIsPending=t=>{this.isPending=t,this.isPendingListeners.forEach(n=>ke(n,t))};getFlagCheck=t=>{let n=v(this.context);return(this.checks[n]??{})[t]};getFlagValue=t=>this.getFlagCheck(t)?.value;addFlagValueListener=(t,n)=>(t in this.flagValueListeners||(this.flagValueListeners[t]=new Set),this.flagValueListeners[t].add(n),()=>{this.flagValueListeners[t].delete(n)});addFlagCheckListener=(t,n)=>(t in this.flagCheckListeners||(this.flagCheckListeners[t]=new Set),this.flagCheckListeners[t].add(n),()=>{this.flagCheckListeners[t].delete(n)});notifyFlagCheckListeners=(t,n)=>{(this.flagCheckListeners?.[t]??[]).forEach(a=>Fe(a,n))};notifyFlagValueListeners=(t,n)=>{(this.flagValueListeners?.[t]??[]).forEach(a=>ve(a,n))}},ke=(r,t)=>{r.length>0?r(t):r()},Fe=(r,t)=>{r.length>0?r(t):r()},ve=(r,t)=>{r.length>0?r(t):r()};window.Schematic=D;})();
1
+ "use strict";(()=>{var ne=Object.create;var M=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var le=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports);var ce=(r,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ae(t))!oe.call(r,i)&&i!==n&&M(r,i,{get:()=>t[i],enumerable:!(s=se(t,i))||s.enumerable});return r};var ue=(r,t,n)=>(n=r!=null?ne(ie(r)):{},ce(t||!r||!r.__esModule?M(n,"default",{value:r,enumerable:!0}):n,r));var z=le(K=>{(function(r){var t=function(n){var s=typeof globalThis<"u"&&globalThis||typeof r<"u"&&r||typeof global<"u"&&global||{},i={searchParams:"URLSearchParams"in s,iterable:"Symbol"in s&&"iterator"in Symbol,blob:"FileReader"in s&&"Blob"in s&&function(){try{return new Blob,!0}catch{return!1}}(),formData:"FormData"in s,arrayBuffer:"ArrayBuffer"in s};function c(e){return e&&DataView.prototype.isPrototypeOf(e)}if(i.arrayBuffer)var u=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],f=ArrayBuffer.isView||function(e){return e&&u.indexOf(Object.prototype.toString.call(e))>-1};function p(e){if(typeof e!="string"&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(e)||e==="")throw new TypeError('Invalid character in header field name: "'+e+'"');return e.toLowerCase()}function k(e){return typeof e!="string"&&(e=String(e)),e}function E(e){var a={next:function(){var o=e.shift();return{done:o===void 0,value:o}}};return i.iterable&&(a[Symbol.iterator]=function(){return a}),a}function d(e){this.map={},e instanceof d?e.forEach(function(a,o){this.append(o,a)},this):Array.isArray(e)?e.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):e&&Object.getOwnPropertyNames(e).forEach(function(a){this.append(a,e[a])},this)}d.prototype.append=function(e,a){e=p(e),a=k(a);var o=this.map[e];this.map[e]=o?o+", "+a:a},d.prototype.delete=function(e){delete this.map[p(e)]},d.prototype.get=function(e){return e=p(e),this.has(e)?this.map[e]:null},d.prototype.has=function(e){return this.map.hasOwnProperty(p(e))},d.prototype.set=function(e,a){this.map[p(e)]=k(a)},d.prototype.forEach=function(e,a){for(var o in this.map)this.map.hasOwnProperty(o)&&e.call(a,this.map[o],o,this)},d.prototype.keys=function(){var e=[];return this.forEach(function(a,o){e.push(o)}),E(e)},d.prototype.values=function(){var e=[];return this.forEach(function(a){e.push(a)}),E(e)},d.prototype.entries=function(){var e=[];return this.forEach(function(a,o){e.push([o,a])}),E(e)},i.iterable&&(d.prototype[Symbol.iterator]=d.prototype.entries);function F(e){if(!e._noBody){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}}function b(e){return new Promise(function(a,o){e.onload=function(){a(e.result)},e.onerror=function(){o(e.error)}})}function V(e){var a=new FileReader,o=b(a);return a.readAsArrayBuffer(e),o}function Q(e){var a=new FileReader,o=b(a),h=/charset=([A-Za-z0-9_-]+)/.exec(e.type),g=h?h[1]:"utf-8";return a.readAsText(e,g),o}function Y(e){for(var a=new Uint8Array(e),o=new Array(a.length),h=0;h<a.length;h++)o[h]=String.fromCharCode(a[h]);return o.join("")}function q(e){if(e.slice)return e.slice(0);var a=new Uint8Array(e.byteLength);return a.set(new Uint8Array(e)),a.buffer}function H(){return this.bodyUsed=!1,this._initBody=function(e){this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?typeof e=="string"?this._bodyText=e:i.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:i.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:i.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():i.arrayBuffer&&i.blob&&c(e)?(this._bodyArrayBuffer=q(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):i.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||f(e))?this._bodyArrayBuffer=q(e):this._bodyText=e=Object.prototype.toString.call(e):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof e=="string"?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):i.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},i.blob&&(this.blob=function(){var e=F(this);if(e)return e;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 e=F(this);return e||(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(i.blob)return this.blob().then(V);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var e=F(this);if(e)return e;if(this._bodyBlob)return Q(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(Y(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},i.formData&&(this.formData=function(){return this.text().then(ee)}),this.json=function(){return this.text().then(JSON.parse)},this}var Z=["CONNECT","DELETE","GET","HEAD","OPTIONS","PATCH","POST","PUT","TRACE"];function j(e){var a=e.toUpperCase();return Z.indexOf(a)>-1?a:e}function C(e,a){if(!(this instanceof C))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');a=a||{};var o=a.body;if(e instanceof C){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,a.headers||(this.headers=new d(e.headers)),this.method=e.method,this.mode=e.mode,this.signal=e.signal,!o&&e._bodyInit!=null&&(o=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=a.credentials||this.credentials||"same-origin",(a.headers||!this.headers)&&(this.headers=new d(a.headers)),this.method=j(a.method||this.method||"GET"),this.mode=a.mode||this.mode||null,this.signal=a.signal||this.signal||function(){if("AbortController"in s){var l=new AbortController;return l.signal}}(),this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&o)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(o),(this.method==="GET"||this.method==="HEAD")&&(a.cache==="no-store"||a.cache==="no-cache")){var h=/([?&])_=[^&]*/;if(h.test(this.url))this.url=this.url.replace(h,"$1_="+new Date().getTime());else{var g=/\?/;this.url+=(g.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}C.prototype.clone=function(){return new C(this,{body:this._bodyInit})};function ee(e){var a=new FormData;return e.trim().split("&").forEach(function(o){if(o){var h=o.split("="),g=h.shift().replace(/\+/g," "),l=h.join("=").replace(/\+/g," ");a.append(decodeURIComponent(g),decodeURIComponent(l))}}),a}function te(e){var a=new d,o=e.replace(/\r?\n[\t ]+/g," ");return o.split("\r").map(function(h){return h.indexOf(`
2
+ `)===0?h.substr(1,h.length):h}).forEach(function(h){var g=h.split(":"),l=g.shift().trim();if(l){var x=g.join(":").trim();try{a.append(l,x)}catch(U){console.warn("Response "+U.message)}}}),a}H.call(C.prototype);function v(e,a){if(!(this instanceof v))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(a||(a={}),this.type="default",this.status=a.status===void 0?200:a.status,this.status<200||this.status>599)throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599].");this.ok=this.status>=200&&this.status<300,this.statusText=a.statusText===void 0?"":""+a.statusText,this.headers=new d(a.headers),this.url=a.url||"",this._initBody(e)}H.call(v.prototype),v.prototype.clone=function(){return new v(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new d(this.headers),url:this.url})},v.error=function(){var e=new v(null,{status:200,statusText:""});return e.ok=!1,e.status=0,e.type="error",e};var re=[301,302,303,307,308];v.redirect=function(e,a){if(re.indexOf(a)===-1)throw new RangeError("Invalid status code");return new v(null,{status:a,headers:{location:e}})},n.DOMException=s.DOMException;try{new n.DOMException}catch{n.DOMException=function(a,o){this.message=a,this.name=o;var h=Error(a);this.stack=h.stack},n.DOMException.prototype=Object.create(Error.prototype),n.DOMException.prototype.constructor=n.DOMException}function I(e,a){return new Promise(function(o,h){var g=new C(e,a);if(g.signal&&g.signal.aborted)return h(new n.DOMException("Aborted","AbortError"));var l=new XMLHttpRequest;function x(){l.abort()}l.onload=function(){var y={statusText:l.statusText,headers:te(l.getAllResponseHeaders()||"")};g.url.indexOf("file://")===0&&(l.status<200||l.status>599)?y.status=200:y.status=l.status,y.url="responseURL"in l?l.responseURL:y.headers.get("X-Request-URL");var R="response"in l?l.response:l.responseText;setTimeout(function(){o(new v(R,y))},0)},l.onerror=function(){setTimeout(function(){h(new TypeError("Network request failed"))},0)},l.ontimeout=function(){setTimeout(function(){h(new TypeError("Network request timed out"))},0)},l.onabort=function(){setTimeout(function(){h(new n.DOMException("Aborted","AbortError"))},0)};function U(y){try{return y===""&&s.location.href?s.location.href:y}catch{return y}}if(l.open(g.method,U(g.url),!0),g.credentials==="include"?l.withCredentials=!0:g.credentials==="omit"&&(l.withCredentials=!1),"responseType"in l&&(i.blob?l.responseType="blob":i.arrayBuffer&&(l.responseType="arraybuffer")),a&&typeof a.headers=="object"&&!(a.headers instanceof d||s.Headers&&a.headers instanceof s.Headers)){var W=[];Object.getOwnPropertyNames(a.headers).forEach(function(y){W.push(p(y)),l.setRequestHeader(y,k(a.headers[y]))}),g.headers.forEach(function(y,R){W.indexOf(R)===-1&&l.setRequestHeader(R,y)})}else g.headers.forEach(function(y,R){l.setRequestHeader(R,y)});g.signal&&(g.signal.addEventListener("abort",x),l.onreadystatechange=function(){l.readyState===4&&g.signal.removeEventListener("abort",x)}),l.send(typeof g._bodyInit>"u"?null:g._bodyInit)})}return I.polyfill=!0,s.fetch||(s.fetch=I,s.Headers=d,s.Request=C,s.Response=v),n.Headers=d,n.Request=C,n.Response=v,n.fetch=I,n}({})})(typeof self<"u"?self:K)});var m=[];for(let r=0;r<256;++r)m.push((r+256).toString(16).slice(1));function $(r,t=0){return(m[r[t+0]]+m[r[t+1]]+m[r[t+2]]+m[r[t+3]]+"-"+m[r[t+4]]+m[r[t+5]]+"-"+m[r[t+6]]+m[r[t+7]]+"-"+m[r[t+8]]+m[r[t+9]]+"-"+m[r[t+10]]+m[r[t+11]]+m[r[t+12]]+m[r[t+13]]+m[r[t+14]]+m[r[t+15]]).toLowerCase()}var P,de=new Uint8Array(16);function A(){if(!P){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");P=crypto.getRandomValues.bind(crypto)}return P(de)}var fe=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),L={randomUUID:fe};function he(r,t,n){if(L.randomUUID&&!t&&!r)return L.randomUUID();r=r||{};let s=r.random??r.rng?.()??A();if(s.length<16)throw new Error("Random bytes length must be >= 16");if(s[6]=s[6]&15|64,s[8]=s[8]&63|128,t){if(n=n||0,n<0||n+16>t.length)throw new RangeError(`UUID byte range ${n}:${n+15} is out of buffer bounds`);for(let i=0;i<16;++i)t[n+i]=s[i];return t}return $(s)}var _=he;var rt=ue(z());function w(r){return ge(r,!1)}function ge(r,t){return r==null?r:{companyId:r.company_id==null?void 0:r.company_id,error:r.error==null?void 0:r.error,featureAllocation:r.feature_allocation==null?void 0:r.feature_allocation,featureUsage:r.feature_usage==null?void 0:r.feature_usage,featureUsagePeriod:r.feature_usage_period==null?void 0:r.feature_usage_period,featureUsageResetAt:r.feature_usage_reset_at==null?void 0:new Date(r.feature_usage_reset_at),flag:r.flag,flagId:r.flag_id==null?void 0:r.flag_id,reason:r.reason,ruleId:r.rule_id==null?void 0:r.rule_id,ruleType:r.rule_type==null?void 0:r.rule_type,userId:r.user_id==null?void 0:r.user_id,value:r.value}}function B(r){return ye(r,!1)}function ye(r,t=!1){return r==null?r:{company_id:r.companyId,error:r.error,flag_id:r.flagId,flag_key:r.flagKey,reason:r.reason,req_company:r.reqCompany,req_user:r.reqUser,rule_id:r.ruleId,user_id:r.userId,value:r.value}}function T(r){return be(r,!1)}function be(r,t){return r==null?r:{data:w(r.data),params:r.params}}function X(r){return ke(r,!1)}function ke(r,t){return r==null?r:{flags:r.flags.map(w)}}function N(r){return Fe(r,!1)}function Fe(r,t){return r==null?r:{data:X(r.data),params:r.params}}var O=r=>{let{companyId:t,error:n,featureAllocation:s,featureUsage:i,featureUsagePeriod:c,featureUsageResetAt:u,flag:f,flagId:p,reason:k,ruleId:E,ruleType:d,userId:F,value:b}=w(r);return{featureUsageExceeded:!b&&(d=="company_override_usage_exceeded"||d=="plan_entitlement_usage_exceeded"),companyId:t??void 0,error:n??void 0,featureAllocation:s??void 0,featureUsage:i??void 0,featureUsagePeriod:c??void 0,featureUsageResetAt:u??void 0,flag:f,flagId:p??void 0,reason:k,ruleId:E??void 0,ruleType:d??void 0,userId:F??void 0,value:b}};function S(r){let t=Object.keys(r).reduce((n,s)=>{let c=Object.keys(r[s]||{}).sort().reduce((u,f)=>(u[f]=r[s][f],u),{});return n[s]=c,n},{});return JSON.stringify(t)}var J="1.2.2";var G="schematicId";var D=class{additionalHeaders={};apiKey;apiUrl="https://api.schematichq.com";conn=null;context={};debugEnabled=!1;offlineEnabled=!1;eventQueue;eventUrl="https://c.schematichq.com";flagCheckListeners={};flagValueListeners={};isPending=!0;isPendingListeners=new Set;storage;useWebSocket=!1;checks={};webSocketUrl="wss://api.schematichq.com";constructor(t,n){if(this.apiKey=t,this.eventQueue=[],this.useWebSocket=n?.useWebSocket??!1,this.debugEnabled=n?.debug??!1,this.offlineEnabled=n?.offline??!1,typeof window<"u"&&typeof window.location<"u"){let s=new URLSearchParams(window.location.search),i=s.get("schematic_debug");i!==null&&(i===""||i==="true"||i==="1")&&(this.debugEnabled=!0);let c=s.get("schematic_offline");c!==null&&(c===""||c==="true"||c==="1")&&(this.offlineEnabled=!0,this.debugEnabled=!0)}this.offlineEnabled&&n?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${J}`,...n?.additionalHeaders??{}},n?.storage?this.storage=n.storage:typeof localStorage<"u"&&(this.storage=localStorage),n?.apiUrl!==void 0&&(this.apiUrl=n.apiUrl),n?.eventUrl!==void 0&&(this.eventUrl=n.eventUrl),n?.webSocketUrl!==void 0&&(this.webSocketUrl=n.webSocketUrl),typeof window<"u"&&window?.addEventListener&&window.addEventListener("beforeunload",()=>{this.flushEventQueue()}),this.offlineEnabled?this.debug("Initialized with offline mode enabled - no network requests will be made"):this.debugEnabled&&this.debug("Initialized with debug mode enabled")}async checkFlag(t){let{fallback:n=!1,key:s}=t,i=t.context||this.context,c=S(i);if(this.debug(`checkFlag: ${s}`,{context:i,fallback:n}),this.isOffline())return this.debug(`checkFlag offline result: ${s}`,{value:n,offlineMode:!0}),n;if(!this.useWebSocket){let u=`${this.apiUrl}/flags/${s}/check`;return fetch(u,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(i)}).then(f=>{if(!f.ok)throw new Error("Network response was not ok");return f.json()}).then(f=>{let p=T(f);this.debug(`checkFlag result: ${s}`,p);let k=O(p.data);return this.submitFlagCheckEvent(s,k,i),k.value}).catch(f=>{console.error("There was a problem with the fetch operation:",f);let p={flag:s,value:n,reason:"API request failed",error:f instanceof Error?f.message:String(f)};return this.submitFlagCheckEvent(s,p,i),n})}try{let u=this.checks[c];if(this.conn!==null&&typeof u<"u"&&typeof u[s]<"u")return this.debug(`checkFlag cached result: ${s}`,u[s]),u[s].value;if(this.isOffline())return n;try{await this.setContext(i)}catch(E){return console.error("WebSocket connection failed, falling back to REST:",E),this.fallbackToRest(s,i,n)}let p=(this.checks[c]??{})[s],k=p?.value??n;return this.debug(`checkFlag WebSocket result: ${s}`,typeof p<"u"?p:{value:n,fallbackUsed:!0}),typeof p<"u"&&this.submitFlagCheckEvent(s,p,i),k}catch(u){console.error("Unexpected error in checkFlag:",u);let f={flag:s,value:n,reason:"Unexpected error in flag check",error:u instanceof Error?u.message:String(u)};return this.submitFlagCheckEvent(s,f,i),n}}debug(t,...n){this.debugEnabled&&console.log(`[Schematic] ${t}`,...n)}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(t,n,s){let i={flagKey:t,value:n.value,reason:n.reason,flagId:n.flagId,ruleId:n.ruleId,companyId:n.companyId,userId:n.userId,error:n.error,reqCompany:s.company,reqUser:s.user};return this.debug("submitting flag check event:",i),this.handleEvent("flag_check",B(i))}async fallbackToRest(t,n,s){if(this.isOffline())return this.debug(`fallbackToRest offline result: ${t}`,{value:s,offlineMode:!0}),s;try{let i=`${this.apiUrl}/flags/${t}/check`,c=await fetch(i,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(n)});if(!c.ok)throw new Error("Network response was not ok");let u=await c.json(),f=T(u);this.debug(`fallbackToRest result: ${t}`,f);let p=O(f.data);return this.submitFlagCheckEvent(t,p,n),p.value}catch(i){console.error("REST API call failed, using fallback value:",i);let c={flag:t,value:s,reason:"API request failed (fallback)",error:i instanceof Error?i.message:String(i)};return this.submitFlagCheckEvent(t,c,n),s}}checkFlags=async t=>{if(t=t||this.context,this.debug("checkFlags",{context:t}),this.isOffline())return this.debug("checkFlags offline result: returning empty object"),{};let n=`${this.apiUrl}/flags/check`,s=JSON.stringify(t);return fetch(n,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:s}).then(i=>{if(!i.ok)throw new Error("Network response was not ok");return i.json()}).then(i=>{let c=N(i);return this.debug("checkFlags result:",c),(c?.data?.flags??[]).reduce((u,f)=>(u[f.flag]=f.value,u),{})}).catch(i=>(console.error("There was a problem with the fetch operation:",i),{}))};identify=t=>{this.debug("identify:",t);try{this.setContext({company:t.company?.keys,user:t.keys})}catch(n){console.error("Error setting context:",n)}return this.handleEvent("identify",t)};setContext=async t=>{if(this.isOffline())return this.context=t,this.setIsPending(!1),Promise.resolve();if(!this.useWebSocket)return Promise.resolve();try{this.setIsPending(!0),this.conn||(this.conn=this.wsConnect());let n=await this.conn;await this.wsSendMessage(n,t)}catch(n){throw console.error("Failed to establish WebSocket connection:",n),n}};track=t=>{let{company:n,user:s,event:i,traits:c}=t,u={company:n??this.context.company,event:i,traits:c??{},user:s??this.context.user};return this.debug("track:",u),this.handleEvent("track",u)};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let t=this.eventQueue.shift();t&&this.sendEvent(t)}};getAnonymousId=()=>{if(!this.storage)return _();let t=this.storage.getItem(G);if(typeof t<"u")return t;let n=_();return this.storage.setItem(G,n),n};handleEvent=(t,n)=>{let s={api_key:this.apiKey,body:n,sent_at:new Date().toISOString(),tracker_event_id:_(),tracker_user_id:this.getAnonymousId(),type:t};return document?.hidden?this.storeEvent(s):this.sendEvent(s)};sendEvent=async t=>{let n=`${this.eventUrl}/e`,s=JSON.stringify(t);if(this.debug("sending event:",{url:n,event:t}),this.isOffline())return this.debug("event not sent (offline mode):",{event:t}),Promise.resolve();try{let i=await fetch(n,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:s});this.debug("event sent:",{status:i.status,statusText:i.statusText})}catch(i){console.error("Error sending Schematic event: ",i)}return Promise.resolve()};storeEvent=t=>(this.eventQueue.push(t),Promise.resolve());cleanup=async()=>{if(this.isOffline())return this.debug("cleanup: skipped (offline mode)"),Promise.resolve();if(this.conn)try{(await this.conn).close()}catch(t){console.error("Error during cleanup:",t)}finally{this.conn=null}};wsConnect=()=>this.isOffline()?(this.debug("wsConnect: skipped (offline mode)"),Promise.reject(new Error("WebSocket connection skipped in offline mode"))):new Promise((t,n)=>{let s=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",s);let i=new WebSocket(s);i.onopen=()=>{this.debug("WebSocket connection opened"),t(i)},i.onerror=c=>{this.debug("WebSocket connection error:",c),n(c)},i.onclose=()=>{this.debug("WebSocket connection closed"),this.conn=null}});wsSendMessage=(t,n)=>this.isOffline()?(this.debug("wsSendMessage: skipped (offline mode)"),this.setIsPending(!1),Promise.resolve()):new Promise((s,i)=>{if(S(n)==S(this.context))return this.debug("WebSocket context unchanged, skipping update"),s(this.setIsPending(!1));this.debug("WebSocket context updated:",n),this.context=n;let c=()=>{let u=!1,f=E=>{let d=JSON.parse(E.data);this.debug("WebSocket message received:",d),S(n)in this.checks||(this.checks[S(n)]={}),(d.flags??[]).forEach(F=>{let b=O(F);this.checks[S(n)][b.flag]=b,this.debug("WebSocket flag update:",{flag:b.flag,value:b.value,flagCheck:b}),(this.flagCheckListeners[F.flag]?.size>0||this.flagValueListeners[F.flag]?.size>0)&&this.submitFlagCheckEvent(b.flag,b,n),this.notifyFlagCheckListeners(F.flag,b),this.notifyFlagValueListeners(F.flag,b.value)}),this.setIsPending(!1),u||(u=!0,s())};t.addEventListener("message",f);let p=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${J}`,k={apiKey:this.apiKey,clientVersion:p,data:n};this.debug("WebSocket sending message:",k),t.send(JSON.stringify(k))};t.readyState===WebSocket.OPEN?(this.debug("WebSocket already open, sending message"),c()):t.readyState===WebSocket.CONNECTING?(this.debug("WebSocket connecting, waiting for open to send message"),t.addEventListener("open",c)):(this.debug("WebSocket is closed, cannot send message"),i("WebSocket is not open or connecting"))});getIsPending=()=>this.isPending;addIsPendingListener=t=>(this.isPendingListeners.add(t),()=>{this.isPendingListeners.delete(t)});setIsPending=t=>{this.isPending=t,this.isPendingListeners.forEach(n=>ve(n,t))};getFlagCheck=t=>{let n=S(this.context);return(this.checks[n]??{})[t]};getFlagValue=t=>this.getFlagCheck(t)?.value;addFlagValueListener=(t,n)=>(t in this.flagValueListeners||(this.flagValueListeners[t]=new Set),this.flagValueListeners[t].add(n),()=>{this.flagValueListeners[t].delete(n)});addFlagCheckListener=(t,n)=>(t in this.flagCheckListeners||(this.flagCheckListeners[t]=new Set),this.flagCheckListeners[t].add(n),()=>{this.flagCheckListeners[t].delete(n)});notifyFlagCheckListeners=(t,n)=>{let s=this.flagCheckListeners?.[t]??[];s.size>0&&this.debug(`Notifying ${s.size} flag check listeners for ${t}`,n),s.forEach(i=>Ee(i,n))};notifyFlagValueListeners=(t,n)=>{let s=this.flagValueListeners?.[t]??[];s.size>0&&this.debug(`Notifying ${s.size} flag value listeners for ${t}`,{value:n}),s.forEach(i=>Ce(i,n))}},ve=(r,t)=>{r.length>0?r(t):r()},Ee=(r,t)=>{r.length>0?r(t):r()},Ce=(r,t)=>{r.length>0?r(t):r()};window.Schematic=D;})();
3
3
  /* @preserve */
@@ -572,6 +572,7 @@ __export(index_exports, {
572
572
  CheckFlagResponseFromJSON: () => CheckFlagResponseFromJSON,
573
573
  CheckFlagReturnFromJSON: () => CheckFlagReturnFromJSON,
574
574
  CheckFlagsResponseFromJSON: () => CheckFlagsResponseFromJSON,
575
+ EventBodyFlagCheckToJSON: () => EventBodyFlagCheckToJSON,
575
576
  RuleType: () => RuleType,
576
577
  Schematic: () => Schematic,
577
578
  UsagePeriod: () => UsagePeriod
@@ -658,6 +659,28 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
658
659
  };
659
660
  }
660
661
 
662
+ // src/types/api/models/EventBodyFlagCheck.ts
663
+ function EventBodyFlagCheckToJSON(json) {
664
+ return EventBodyFlagCheckToJSONTyped(json, false);
665
+ }
666
+ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
667
+ if (value == null) {
668
+ return value;
669
+ }
670
+ return {
671
+ company_id: value["companyId"],
672
+ error: value["error"],
673
+ flag_id: value["flagId"],
674
+ flag_key: value["flagKey"],
675
+ reason: value["reason"],
676
+ req_company: value["reqCompany"],
677
+ req_user: value["reqUser"],
678
+ rule_id: value["ruleId"],
679
+ user_id: value["userId"],
680
+ value: value["value"]
681
+ };
682
+ }
683
+
661
684
  // src/types/api/models/CheckFlagResponse.ts
662
685
  function CheckFlagResponseFromJSON(json) {
663
686
  return CheckFlagResponseFromJSONTyped(json, false);
@@ -771,7 +794,7 @@ function contextString(context) {
771
794
  }
772
795
 
773
796
  // src/version.ts
774
- var version = "1.2.1";
797
+ var version = "1.2.2";
775
798
 
776
799
  // src/index.ts
777
800
  var anonymousIdKey = "schematicId";
@@ -781,6 +804,8 @@ var Schematic = class {
781
804
  apiUrl = "https://api.schematichq.com";
782
805
  conn = null;
783
806
  context = {};
807
+ debugEnabled = false;
808
+ offlineEnabled = false;
784
809
  eventQueue;
785
810
  eventUrl = "https://c.schematichq.com";
786
811
  flagCheckListeners = {};
@@ -795,6 +820,26 @@ var Schematic = class {
795
820
  this.apiKey = apiKey;
796
821
  this.eventQueue = [];
797
822
  this.useWebSocket = options?.useWebSocket ?? false;
823
+ this.debugEnabled = options?.debug ?? false;
824
+ this.offlineEnabled = options?.offline ?? false;
825
+ if (typeof window !== "undefined" && typeof window.location !== "undefined") {
826
+ const params = new URLSearchParams(window.location.search);
827
+ const debugParam = params.get("schematic_debug");
828
+ if (debugParam !== null && (debugParam === "" || debugParam === "true" || debugParam === "1")) {
829
+ this.debugEnabled = true;
830
+ }
831
+ const offlineParam = params.get("schematic_offline");
832
+ if (offlineParam !== null && (offlineParam === "" || offlineParam === "true" || offlineParam === "1")) {
833
+ this.offlineEnabled = true;
834
+ this.debugEnabled = true;
835
+ }
836
+ }
837
+ if (this.offlineEnabled && options?.debug !== false) {
838
+ this.debugEnabled = true;
839
+ }
840
+ if (this.offlineEnabled) {
841
+ this.setIsPending(false);
842
+ }
798
843
  this.additionalHeaders = {
799
844
  "X-Schematic-Client-Version": `schematic-js@${version}`,
800
845
  ...options?.additionalHeaders ?? {}
@@ -818,6 +863,13 @@ var Schematic = class {
818
863
  this.flushEventQueue();
819
864
  });
820
865
  }
866
+ if (this.offlineEnabled) {
867
+ this.debug(
868
+ "Initialized with offline mode enabled - no network requests will be made"
869
+ );
870
+ } else if (this.debugEnabled) {
871
+ this.debug("Initialized with debug mode enabled");
872
+ }
821
873
  }
822
874
  /**
823
875
  * Get value for a single flag.
@@ -830,6 +882,14 @@ var Schematic = class {
830
882
  const { fallback = false, key } = options;
831
883
  const context = options.context || this.context;
832
884
  const contextStr = contextString(context);
885
+ this.debug(`checkFlag: ${key}`, { context, fallback });
886
+ if (this.isOffline()) {
887
+ this.debug(`checkFlag offline result: ${key}`, {
888
+ value: fallback,
889
+ offlineMode: true
890
+ });
891
+ return fallback;
892
+ }
833
893
  if (!this.useWebSocket) {
834
894
  const requestUrl = `${this.apiUrl}/flags/${key}/check`;
835
895
  return fetch(requestUrl, {
@@ -846,17 +906,32 @@ var Schematic = class {
846
906
  }
847
907
  return response.json();
848
908
  }).then((response) => {
849
- return CheckFlagResponseFromJSON(response).data.value;
909
+ const parsedResponse = CheckFlagResponseFromJSON(response);
910
+ this.debug(`checkFlag result: ${key}`, parsedResponse);
911
+ const result = CheckFlagReturnFromJSON(parsedResponse.data);
912
+ this.submitFlagCheckEvent(key, result, context);
913
+ return result.value;
850
914
  }).catch((error) => {
851
915
  console.error("There was a problem with the fetch operation:", error);
916
+ const errorResult = {
917
+ flag: key,
918
+ value: fallback,
919
+ reason: "API request failed",
920
+ error: error instanceof Error ? error.message : String(error)
921
+ };
922
+ this.submitFlagCheckEvent(key, errorResult, context);
852
923
  return fallback;
853
924
  });
854
925
  }
855
926
  try {
856
927
  const existingVals = this.checks[contextStr];
857
- if (this.conn && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
928
+ if (this.conn !== null && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
929
+ this.debug(`checkFlag cached result: ${key}`, existingVals[key]);
858
930
  return existingVals[key].value;
859
931
  }
932
+ if (this.isOffline()) {
933
+ return fallback;
934
+ }
860
935
  try {
861
936
  await this.setContext(context);
862
937
  } catch (error) {
@@ -867,16 +942,74 @@ var Schematic = class {
867
942
  return this.fallbackToRest(key, context, fallback);
868
943
  }
869
944
  const contextVals = this.checks[contextStr] ?? {};
870
- return contextVals[key]?.value ?? fallback;
945
+ const flagCheck = contextVals[key];
946
+ const result = flagCheck?.value ?? fallback;
947
+ this.debug(
948
+ `checkFlag WebSocket result: ${key}`,
949
+ typeof flagCheck !== "undefined" ? flagCheck : { value: fallback, fallbackUsed: true }
950
+ );
951
+ if (typeof flagCheck !== "undefined") {
952
+ this.submitFlagCheckEvent(key, flagCheck, context);
953
+ }
954
+ return result;
871
955
  } catch (error) {
872
956
  console.error("Unexpected error in checkFlag:", error);
957
+ const errorResult = {
958
+ flag: key,
959
+ value: fallback,
960
+ reason: "Unexpected error in flag check",
961
+ error: error instanceof Error ? error.message : String(error)
962
+ };
963
+ this.submitFlagCheckEvent(key, errorResult, context);
873
964
  return fallback;
874
965
  }
875
966
  }
967
+ /**
968
+ * Helper function to log debug messages
969
+ * Only logs if debug mode is enabled
970
+ */
971
+ debug(message, ...args) {
972
+ if (this.debugEnabled) {
973
+ console.log(`[Schematic] ${message}`, ...args);
974
+ }
975
+ }
976
+ /**
977
+ * Helper function to check if client is in offline mode
978
+ */
979
+ isOffline() {
980
+ return this.offlineEnabled;
981
+ }
982
+ /**
983
+ * Submit a flag check event
984
+ * Records data about a flag check for analytics
985
+ */
986
+ submitFlagCheckEvent(flagKey, result, context) {
987
+ const eventBody = {
988
+ flagKey,
989
+ value: result.value,
990
+ reason: result.reason,
991
+ flagId: result.flagId,
992
+ ruleId: result.ruleId,
993
+ companyId: result.companyId,
994
+ userId: result.userId,
995
+ error: result.error,
996
+ reqCompany: context.company,
997
+ reqUser: context.user
998
+ };
999
+ this.debug(`submitting flag check event:`, eventBody);
1000
+ return this.handleEvent("flag_check", EventBodyFlagCheckToJSON(eventBody));
1001
+ }
876
1002
  /**
877
1003
  * Helper method for falling back to REST API when WebSocket connection fails
878
1004
  */
879
1005
  async fallbackToRest(key, context, fallback) {
1006
+ if (this.isOffline()) {
1007
+ this.debug(`fallbackToRest offline result: ${key}`, {
1008
+ value: fallback,
1009
+ offlineMode: true
1010
+ });
1011
+ return fallback;
1012
+ }
880
1013
  try {
881
1014
  const requestUrl = `${this.apiUrl}/flags/${key}/check`;
882
1015
  const response = await fetch(requestUrl, {
@@ -891,19 +1024,36 @@ var Schematic = class {
891
1024
  if (!response.ok) {
892
1025
  throw new Error("Network response was not ok");
893
1026
  }
894
- const data = CheckFlagResponseFromJSON(await response.json());
895
- return data?.data?.value ?? false;
1027
+ const responseJson = await response.json();
1028
+ const data = CheckFlagResponseFromJSON(responseJson);
1029
+ this.debug(`fallbackToRest result: ${key}`, data);
1030
+ const result = CheckFlagReturnFromJSON(data.data);
1031
+ this.submitFlagCheckEvent(key, result, context);
1032
+ return result.value;
896
1033
  } catch (error) {
897
1034
  console.error("REST API call failed, using fallback value:", error);
1035
+ const errorResult = {
1036
+ flag: key,
1037
+ value: fallback,
1038
+ reason: "API request failed (fallback)",
1039
+ error: error instanceof Error ? error.message : String(error)
1040
+ };
1041
+ this.submitFlagCheckEvent(key, errorResult, context);
898
1042
  return fallback;
899
1043
  }
900
1044
  }
901
1045
  /**
902
1046
  * Make an API call to fetch all flag values for a given context.
903
1047
  * Recommended for use in REST mode only.
1048
+ * In offline mode, returns an empty object.
904
1049
  */
905
1050
  checkFlags = async (context) => {
906
1051
  context = context || this.context;
1052
+ this.debug(`checkFlags`, { context });
1053
+ if (this.isOffline()) {
1054
+ this.debug(`checkFlags offline result: returning empty object`);
1055
+ return {};
1056
+ }
907
1057
  const requestUrl = `${this.apiUrl}/flags/check`;
908
1058
  const requestBody = JSON.stringify(context);
909
1059
  return fetch(requestUrl, {
@@ -921,6 +1071,7 @@ var Schematic = class {
921
1071
  return response.json();
922
1072
  }).then((responseJson) => {
923
1073
  const resp = CheckFlagsResponseFromJSON(responseJson);
1074
+ this.debug(`checkFlags result:`, resp);
924
1075
  return (resp?.data?.flags ?? []).reduce(
925
1076
  (accum, flag) => {
926
1077
  accum[flag.flag] = flag.value;
@@ -939,6 +1090,7 @@ var Schematic = class {
939
1090
  * send an identify event to the Schematic API which will upsert a user and company.
940
1091
  */
941
1092
  identify = (body) => {
1093
+ this.debug(`identify:`, body);
942
1094
  try {
943
1095
  this.setContext({
944
1096
  company: body.company?.keys,
@@ -956,10 +1108,15 @@ var Schematic = class {
956
1108
  * 2. Send the context to the server
957
1109
  * 3. Wait for initial flag values to be returned
958
1110
  * The promise resolves when initial flag values are received.
1111
+ * In offline mode, this will just set the context locally without connecting.
959
1112
  */
960
1113
  setContext = async (context) => {
961
- if (!this.useWebSocket) {
1114
+ if (this.isOffline()) {
962
1115
  this.context = context;
1116
+ this.setIsPending(false);
1117
+ return Promise.resolve();
1118
+ }
1119
+ if (!this.useWebSocket) {
963
1120
  return Promise.resolve();
964
1121
  }
965
1122
  try {
@@ -980,12 +1137,14 @@ var Schematic = class {
980
1137
  */
981
1138
  track = (body) => {
982
1139
  const { company, user, event, traits } = body;
983
- return this.handleEvent("track", {
1140
+ const trackData = {
984
1141
  company: company ?? this.context.company,
985
1142
  event,
986
1143
  traits: traits ?? {},
987
1144
  user: user ?? this.context.user
988
- });
1145
+ };
1146
+ this.debug(`track:`, trackData);
1147
+ return this.handleEvent("track", trackData);
989
1148
  };
990
1149
  /**
991
1150
  * Event processing
@@ -1028,8 +1187,13 @@ var Schematic = class {
1028
1187
  sendEvent = async (event) => {
1029
1188
  const captureUrl = `${this.eventUrl}/e`;
1030
1189
  const payload = JSON.stringify(event);
1190
+ this.debug(`sending event:`, { url: captureUrl, event });
1191
+ if (this.isOffline()) {
1192
+ this.debug(`event not sent (offline mode):`, { event });
1193
+ return Promise.resolve();
1194
+ }
1031
1195
  try {
1032
- await fetch(captureUrl, {
1196
+ const response = await fetch(captureUrl, {
1033
1197
  method: "POST",
1034
1198
  headers: {
1035
1199
  ...this.additionalHeaders ?? {},
@@ -1037,6 +1201,10 @@ var Schematic = class {
1037
1201
  },
1038
1202
  body: payload
1039
1203
  });
1204
+ this.debug(`event sent:`, {
1205
+ status: response.status,
1206
+ statusText: response.statusText
1207
+ });
1040
1208
  } catch (error) {
1041
1209
  console.error("Error sending Schematic event: ", error);
1042
1210
  }
@@ -1051,8 +1219,13 @@ var Schematic = class {
1051
1219
  */
1052
1220
  /**
1053
1221
  * If using websocket mode, close the connection when done.
1222
+ * In offline mode, this is a no-op.
1054
1223
  */
1055
1224
  cleanup = async () => {
1225
+ if (this.isOffline()) {
1226
+ this.debug("cleanup: skipped (offline mode)");
1227
+ return Promise.resolve();
1228
+ }
1056
1229
  if (this.conn) {
1057
1230
  try {
1058
1231
  const socket = await this.conn;
@@ -1066,16 +1239,26 @@ var Schematic = class {
1066
1239
  };
1067
1240
  // Open a websocket connection
1068
1241
  wsConnect = () => {
1242
+ if (this.isOffline()) {
1243
+ this.debug("wsConnect: skipped (offline mode)");
1244
+ return Promise.reject(
1245
+ new Error("WebSocket connection skipped in offline mode")
1246
+ );
1247
+ }
1069
1248
  return new Promise((resolve, reject) => {
1070
1249
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1250
+ this.debug(`connecting to WebSocket:`, wsUrl);
1071
1251
  const webSocket = new WebSocket(wsUrl);
1072
1252
  webSocket.onopen = () => {
1253
+ this.debug(`WebSocket connection opened`);
1073
1254
  resolve(webSocket);
1074
1255
  };
1075
1256
  webSocket.onerror = (error) => {
1257
+ this.debug(`WebSocket connection error:`, error);
1076
1258
  reject(error);
1077
1259
  };
1078
1260
  webSocket.onclose = () => {
1261
+ this.debug(`WebSocket connection closed`);
1079
1262
  this.conn = null;
1080
1263
  };
1081
1264
  });
@@ -1083,21 +1266,37 @@ var Schematic = class {
1083
1266
  // Send a message on the websocket indicating interest in a particular evaluation context
1084
1267
  // and wait for the initial set of flag values to be returned
1085
1268
  wsSendMessage = (socket, context) => {
1269
+ if (this.isOffline()) {
1270
+ this.debug("wsSendMessage: skipped (offline mode)");
1271
+ this.setIsPending(false);
1272
+ return Promise.resolve();
1273
+ }
1086
1274
  return new Promise((resolve, reject) => {
1087
1275
  if (contextString(context) == contextString(this.context)) {
1276
+ this.debug(`WebSocket context unchanged, skipping update`);
1088
1277
  return resolve(this.setIsPending(false));
1089
1278
  }
1279
+ this.debug(`WebSocket context updated:`, context);
1090
1280
  this.context = context;
1091
1281
  const sendMessage = () => {
1092
1282
  let resolved = false;
1093
1283
  const messageHandler = (event) => {
1094
1284
  const message = JSON.parse(event.data);
1285
+ this.debug(`WebSocket message received:`, message);
1095
1286
  if (!(contextString(context) in this.checks)) {
1096
1287
  this.checks[contextString(context)] = {};
1097
1288
  }
1098
1289
  (message.flags ?? []).forEach((flag) => {
1099
1290
  const flagCheck = CheckFlagReturnFromJSON(flag);
1100
1291
  this.checks[contextString(context)][flagCheck.flag] = flagCheck;
1292
+ this.debug(`WebSocket flag update:`, {
1293
+ flag: flagCheck.flag,
1294
+ value: flagCheck.value,
1295
+ flagCheck
1296
+ });
1297
+ if (this.flagCheckListeners[flag.flag]?.size > 0 || this.flagValueListeners[flag.flag]?.size > 0) {
1298
+ this.submitFlagCheckEvent(flagCheck.flag, flagCheck, context);
1299
+ }
1101
1300
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1102
1301
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1103
1302
  });
@@ -1108,19 +1307,23 @@ var Schematic = class {
1108
1307
  }
1109
1308
  };
1110
1309
  socket.addEventListener("message", messageHandler);
1111
- socket.send(
1112
- JSON.stringify({
1113
- apiKey: this.apiKey,
1114
- clientVersion: `schematic-js@${version}`,
1115
- data: context
1116
- })
1117
- );
1310
+ const clientVersion = this.additionalHeaders["X-Schematic-Client-Version"] ?? `schematic-js@${version}`;
1311
+ const messagePayload = {
1312
+ apiKey: this.apiKey,
1313
+ clientVersion,
1314
+ data: context
1315
+ };
1316
+ this.debug(`WebSocket sending message:`, messagePayload);
1317
+ socket.send(JSON.stringify(messagePayload));
1118
1318
  };
1119
1319
  if (socket.readyState === WebSocket.OPEN) {
1320
+ this.debug(`WebSocket already open, sending message`);
1120
1321
  sendMessage();
1121
1322
  } else if (socket.readyState === WebSocket.CONNECTING) {
1323
+ this.debug(`WebSocket connecting, waiting for open to send message`);
1122
1324
  socket.addEventListener("open", sendMessage);
1123
1325
  } else {
1326
+ this.debug(`WebSocket is closed, cannot send message`);
1124
1327
  reject("WebSocket is not open or connecting");
1125
1328
  }
1126
1329
  });
@@ -1177,10 +1380,22 @@ var Schematic = class {
1177
1380
  };
1178
1381
  notifyFlagCheckListeners = (flagKey, check) => {
1179
1382
  const listeners = this.flagCheckListeners?.[flagKey] ?? [];
1383
+ if (listeners.size > 0) {
1384
+ this.debug(
1385
+ `Notifying ${listeners.size} flag check listeners for ${flagKey}`,
1386
+ check
1387
+ );
1388
+ }
1180
1389
  listeners.forEach((listener) => notifyFlagCheckListener(listener, check));
1181
1390
  };
1182
1391
  notifyFlagValueListeners = (flagKey, value) => {
1183
1392
  const listeners = this.flagValueListeners?.[flagKey] ?? [];
1393
+ if (listeners.size > 0) {
1394
+ this.debug(
1395
+ `Notifying ${listeners.size} flag value listeners for ${flagKey}`,
1396
+ { value }
1397
+ );
1398
+ }
1184
1399
  listeners.forEach((listener) => notifyFlagValueListener(listener, value));
1185
1400
  };
1186
1401
  };
@@ -208,7 +208,92 @@ declare type Event_2 = {
208
208
  };
209
209
  export { Event_2 as Event }
210
210
 
211
- export declare type EventBody = EventBodyIdentify | EventBodyTrack;
211
+ export declare type EventBody = EventBodyIdentify | EventBodyTrack | EventBodyFlagCheck;
212
+
213
+ /**
214
+ * Schematic API
215
+ * Schematic API
216
+ *
217
+ * The version of the OpenAPI document: 0.1
218
+ *
219
+ *
220
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
221
+ * https://openapi-generator.tech
222
+ * Do not edit the class manually.
223
+ */
224
+ /**
225
+ *
226
+ * @export
227
+ * @interface EventBodyFlagCheck
228
+ */
229
+ export declare interface EventBodyFlagCheck {
230
+ /**
231
+ * Schematic company ID (starting with 'comp_') of the company evaluated, if any
232
+ * @type {string}
233
+ * @memberof EventBodyFlagCheck
234
+ */
235
+ companyId?: string | null;
236
+ /**
237
+ * Report an error that occurred during the flag check
238
+ * @type {string}
239
+ * @memberof EventBodyFlagCheck
240
+ */
241
+ error?: string | null;
242
+ /**
243
+ * Schematic flag ID (starting with 'flag_') for the flag matching the key, if any
244
+ * @type {string}
245
+ * @memberof EventBodyFlagCheck
246
+ */
247
+ flagId?: string | null;
248
+ /**
249
+ * The key of the flag being checked
250
+ * @type {string}
251
+ * @memberof EventBodyFlagCheck
252
+ */
253
+ flagKey: string;
254
+ /**
255
+ * The reason why the value was returned
256
+ * @type {string}
257
+ * @memberof EventBodyFlagCheck
258
+ */
259
+ reason: string;
260
+ /**
261
+ * Key-value pairs used to to identify company for which the flag was checked
262
+ * @type {{ [key: string]: string; }}
263
+ * @memberof EventBodyFlagCheck
264
+ */
265
+ reqCompany?: {
266
+ [key: string]: string;
267
+ } | null;
268
+ /**
269
+ * Key-value pairs used to to identify user for which the flag was checked
270
+ * @type {{ [key: string]: string; }}
271
+ * @memberof EventBodyFlagCheck
272
+ */
273
+ reqUser?: {
274
+ [key: string]: string;
275
+ } | null;
276
+ /**
277
+ * Schematic rule ID (starting with 'rule_') of the rule that matched for the flag, if any
278
+ * @type {string}
279
+ * @memberof EventBodyFlagCheck
280
+ */
281
+ ruleId?: string | null;
282
+ /**
283
+ * Schematic user ID (starting with 'user_') of the user evaluated, if any
284
+ * @type {string}
285
+ * @memberof EventBodyFlagCheck
286
+ */
287
+ userId?: string | null;
288
+ /**
289
+ * The value of the flag for the given company and/or user
290
+ * @type {boolean}
291
+ * @memberof EventBodyFlagCheck
292
+ */
293
+ value: boolean;
294
+ }
295
+
296
+ export declare function EventBodyFlagCheckToJSON(json: any): EventBodyFlagCheck;
212
297
 
213
298
  export declare type EventBodyIdentify = {
214
299
  company?: {
@@ -227,7 +312,7 @@ export declare type EventBodyTrack = SchematicContext & {
227
312
  traits?: Traits;
228
313
  };
229
314
 
230
- export declare type EventType = "identify" | "track";
315
+ export declare type EventType = "identify" | "track" | "flag_check";
231
316
 
232
317
  export declare type FlagCheckListenerFn = CheckFlagReturnListenerFn | EmptyListenerFn;
233
318
 
@@ -261,6 +346,8 @@ export declare class Schematic {
261
346
  private apiUrl;
262
347
  private conn;
263
348
  private context;
349
+ private debugEnabled;
350
+ private offlineEnabled;
264
351
  private eventQueue;
265
352
  private eventUrl;
266
353
  private flagCheckListeners;
@@ -280,6 +367,20 @@ export declare class Schematic {
280
367
  * In REST mode, makes an API call for each check.
281
368
  */
282
369
  checkFlag(options: CheckOptions): Promise<boolean>;
370
+ /**
371
+ * Helper function to log debug messages
372
+ * Only logs if debug mode is enabled
373
+ */
374
+ private debug;
375
+ /**
376
+ * Helper function to check if client is in offline mode
377
+ */
378
+ private isOffline;
379
+ /**
380
+ * Submit a flag check event
381
+ * Records data about a flag check for analytics
382
+ */
383
+ private submitFlagCheckEvent;
283
384
  /**
284
385
  * Helper method for falling back to REST API when WebSocket connection fails
285
386
  */
@@ -287,6 +388,7 @@ export declare class Schematic {
287
388
  /**
288
389
  * Make an API call to fetch all flag values for a given context.
289
390
  * Recommended for use in REST mode only.
391
+ * In offline mode, returns an empty object.
290
392
  */
291
393
  checkFlags: (context?: SchematicContext) => Promise<Record<string, boolean>>;
292
394
  /**
@@ -302,6 +404,7 @@ export declare class Schematic {
302
404
  * 2. Send the context to the server
303
405
  * 3. Wait for initial flag values to be returned
304
406
  * The promise resolves when initial flag values are received.
407
+ * In offline mode, this will just set the context locally without connecting.
305
408
  */
306
409
  setContext: (context: SchematicContext) => Promise<void>;
307
410
  /**
@@ -322,6 +425,7 @@ export declare class Schematic {
322
425
  */
323
426
  /**
324
427
  * If using websocket mode, close the connection when done.
428
+ * In offline mode, this is a no-op.
325
429
  */
326
430
  cleanup: () => Promise<void>;
327
431
  private wsConnect;
@@ -353,8 +457,15 @@ export declare type SchematicOptions = {
353
457
  additionalHeaders?: Record<string, string>;
354
458
  /** Optionally provide a custom API URL */
355
459
  apiUrl?: string;
460
+ /** Enable debug mode to log flag check results and events to the console.
461
+ * Can also be enabled at runtime via URL query parameter "schematic_debug=true" */
462
+ debug?: boolean;
356
463
  /** Optionally provide a custom event URL */
357
464
  eventUrl?: string;
465
+ /** Enable offline mode to prevent all network requests.
466
+ * When enabled, events are only logged not sent, and flag checks return fallback values.
467
+ * Can also be enabled at runtime via URL query parameter "schematic_offline=true" */
468
+ offline?: boolean;
358
469
  /** Optionally provide a custom storage persister for client-side storage */
359
470
  storage?: StoragePersister;
360
471
  /** Use a WebSocket connection for real-time flag checks; if using this, run the cleanup function to close the connection */
@@ -640,6 +640,28 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
640
640
  };
641
641
  }
642
642
 
643
+ // src/types/api/models/EventBodyFlagCheck.ts
644
+ function EventBodyFlagCheckToJSON(json) {
645
+ return EventBodyFlagCheckToJSONTyped(json, false);
646
+ }
647
+ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
648
+ if (value == null) {
649
+ return value;
650
+ }
651
+ return {
652
+ company_id: value["companyId"],
653
+ error: value["error"],
654
+ flag_id: value["flagId"],
655
+ flag_key: value["flagKey"],
656
+ reason: value["reason"],
657
+ req_company: value["reqCompany"],
658
+ req_user: value["reqUser"],
659
+ rule_id: value["ruleId"],
660
+ user_id: value["userId"],
661
+ value: value["value"]
662
+ };
663
+ }
664
+
643
665
  // src/types/api/models/CheckFlagResponse.ts
644
666
  function CheckFlagResponseFromJSON(json) {
645
667
  return CheckFlagResponseFromJSONTyped(json, false);
@@ -753,7 +775,7 @@ function contextString(context) {
753
775
  }
754
776
 
755
777
  // src/version.ts
756
- var version = "1.2.1";
778
+ var version = "1.2.2";
757
779
 
758
780
  // src/index.ts
759
781
  var anonymousIdKey = "schematicId";
@@ -763,6 +785,8 @@ var Schematic = class {
763
785
  apiUrl = "https://api.schematichq.com";
764
786
  conn = null;
765
787
  context = {};
788
+ debugEnabled = false;
789
+ offlineEnabled = false;
766
790
  eventQueue;
767
791
  eventUrl = "https://c.schematichq.com";
768
792
  flagCheckListeners = {};
@@ -777,6 +801,26 @@ var Schematic = class {
777
801
  this.apiKey = apiKey;
778
802
  this.eventQueue = [];
779
803
  this.useWebSocket = options?.useWebSocket ?? false;
804
+ this.debugEnabled = options?.debug ?? false;
805
+ this.offlineEnabled = options?.offline ?? false;
806
+ if (typeof window !== "undefined" && typeof window.location !== "undefined") {
807
+ const params = new URLSearchParams(window.location.search);
808
+ const debugParam = params.get("schematic_debug");
809
+ if (debugParam !== null && (debugParam === "" || debugParam === "true" || debugParam === "1")) {
810
+ this.debugEnabled = true;
811
+ }
812
+ const offlineParam = params.get("schematic_offline");
813
+ if (offlineParam !== null && (offlineParam === "" || offlineParam === "true" || offlineParam === "1")) {
814
+ this.offlineEnabled = true;
815
+ this.debugEnabled = true;
816
+ }
817
+ }
818
+ if (this.offlineEnabled && options?.debug !== false) {
819
+ this.debugEnabled = true;
820
+ }
821
+ if (this.offlineEnabled) {
822
+ this.setIsPending(false);
823
+ }
780
824
  this.additionalHeaders = {
781
825
  "X-Schematic-Client-Version": `schematic-js@${version}`,
782
826
  ...options?.additionalHeaders ?? {}
@@ -800,6 +844,13 @@ var Schematic = class {
800
844
  this.flushEventQueue();
801
845
  });
802
846
  }
847
+ if (this.offlineEnabled) {
848
+ this.debug(
849
+ "Initialized with offline mode enabled - no network requests will be made"
850
+ );
851
+ } else if (this.debugEnabled) {
852
+ this.debug("Initialized with debug mode enabled");
853
+ }
803
854
  }
804
855
  /**
805
856
  * Get value for a single flag.
@@ -812,6 +863,14 @@ var Schematic = class {
812
863
  const { fallback = false, key } = options;
813
864
  const context = options.context || this.context;
814
865
  const contextStr = contextString(context);
866
+ this.debug(`checkFlag: ${key}`, { context, fallback });
867
+ if (this.isOffline()) {
868
+ this.debug(`checkFlag offline result: ${key}`, {
869
+ value: fallback,
870
+ offlineMode: true
871
+ });
872
+ return fallback;
873
+ }
815
874
  if (!this.useWebSocket) {
816
875
  const requestUrl = `${this.apiUrl}/flags/${key}/check`;
817
876
  return fetch(requestUrl, {
@@ -828,17 +887,32 @@ var Schematic = class {
828
887
  }
829
888
  return response.json();
830
889
  }).then((response) => {
831
- return CheckFlagResponseFromJSON(response).data.value;
890
+ const parsedResponse = CheckFlagResponseFromJSON(response);
891
+ this.debug(`checkFlag result: ${key}`, parsedResponse);
892
+ const result = CheckFlagReturnFromJSON(parsedResponse.data);
893
+ this.submitFlagCheckEvent(key, result, context);
894
+ return result.value;
832
895
  }).catch((error) => {
833
896
  console.error("There was a problem with the fetch operation:", error);
897
+ const errorResult = {
898
+ flag: key,
899
+ value: fallback,
900
+ reason: "API request failed",
901
+ error: error instanceof Error ? error.message : String(error)
902
+ };
903
+ this.submitFlagCheckEvent(key, errorResult, context);
834
904
  return fallback;
835
905
  });
836
906
  }
837
907
  try {
838
908
  const existingVals = this.checks[contextStr];
839
- if (this.conn && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
909
+ if (this.conn !== null && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
910
+ this.debug(`checkFlag cached result: ${key}`, existingVals[key]);
840
911
  return existingVals[key].value;
841
912
  }
913
+ if (this.isOffline()) {
914
+ return fallback;
915
+ }
842
916
  try {
843
917
  await this.setContext(context);
844
918
  } catch (error) {
@@ -849,16 +923,74 @@ var Schematic = class {
849
923
  return this.fallbackToRest(key, context, fallback);
850
924
  }
851
925
  const contextVals = this.checks[contextStr] ?? {};
852
- return contextVals[key]?.value ?? fallback;
926
+ const flagCheck = contextVals[key];
927
+ const result = flagCheck?.value ?? fallback;
928
+ this.debug(
929
+ `checkFlag WebSocket result: ${key}`,
930
+ typeof flagCheck !== "undefined" ? flagCheck : { value: fallback, fallbackUsed: true }
931
+ );
932
+ if (typeof flagCheck !== "undefined") {
933
+ this.submitFlagCheckEvent(key, flagCheck, context);
934
+ }
935
+ return result;
853
936
  } catch (error) {
854
937
  console.error("Unexpected error in checkFlag:", error);
938
+ const errorResult = {
939
+ flag: key,
940
+ value: fallback,
941
+ reason: "Unexpected error in flag check",
942
+ error: error instanceof Error ? error.message : String(error)
943
+ };
944
+ this.submitFlagCheckEvent(key, errorResult, context);
855
945
  return fallback;
856
946
  }
857
947
  }
948
+ /**
949
+ * Helper function to log debug messages
950
+ * Only logs if debug mode is enabled
951
+ */
952
+ debug(message, ...args) {
953
+ if (this.debugEnabled) {
954
+ console.log(`[Schematic] ${message}`, ...args);
955
+ }
956
+ }
957
+ /**
958
+ * Helper function to check if client is in offline mode
959
+ */
960
+ isOffline() {
961
+ return this.offlineEnabled;
962
+ }
963
+ /**
964
+ * Submit a flag check event
965
+ * Records data about a flag check for analytics
966
+ */
967
+ submitFlagCheckEvent(flagKey, result, context) {
968
+ const eventBody = {
969
+ flagKey,
970
+ value: result.value,
971
+ reason: result.reason,
972
+ flagId: result.flagId,
973
+ ruleId: result.ruleId,
974
+ companyId: result.companyId,
975
+ userId: result.userId,
976
+ error: result.error,
977
+ reqCompany: context.company,
978
+ reqUser: context.user
979
+ };
980
+ this.debug(`submitting flag check event:`, eventBody);
981
+ return this.handleEvent("flag_check", EventBodyFlagCheckToJSON(eventBody));
982
+ }
858
983
  /**
859
984
  * Helper method for falling back to REST API when WebSocket connection fails
860
985
  */
861
986
  async fallbackToRest(key, context, fallback) {
987
+ if (this.isOffline()) {
988
+ this.debug(`fallbackToRest offline result: ${key}`, {
989
+ value: fallback,
990
+ offlineMode: true
991
+ });
992
+ return fallback;
993
+ }
862
994
  try {
863
995
  const requestUrl = `${this.apiUrl}/flags/${key}/check`;
864
996
  const response = await fetch(requestUrl, {
@@ -873,19 +1005,36 @@ var Schematic = class {
873
1005
  if (!response.ok) {
874
1006
  throw new Error("Network response was not ok");
875
1007
  }
876
- const data = CheckFlagResponseFromJSON(await response.json());
877
- return data?.data?.value ?? false;
1008
+ const responseJson = await response.json();
1009
+ const data = CheckFlagResponseFromJSON(responseJson);
1010
+ this.debug(`fallbackToRest result: ${key}`, data);
1011
+ const result = CheckFlagReturnFromJSON(data.data);
1012
+ this.submitFlagCheckEvent(key, result, context);
1013
+ return result.value;
878
1014
  } catch (error) {
879
1015
  console.error("REST API call failed, using fallback value:", error);
1016
+ const errorResult = {
1017
+ flag: key,
1018
+ value: fallback,
1019
+ reason: "API request failed (fallback)",
1020
+ error: error instanceof Error ? error.message : String(error)
1021
+ };
1022
+ this.submitFlagCheckEvent(key, errorResult, context);
880
1023
  return fallback;
881
1024
  }
882
1025
  }
883
1026
  /**
884
1027
  * Make an API call to fetch all flag values for a given context.
885
1028
  * Recommended for use in REST mode only.
1029
+ * In offline mode, returns an empty object.
886
1030
  */
887
1031
  checkFlags = async (context) => {
888
1032
  context = context || this.context;
1033
+ this.debug(`checkFlags`, { context });
1034
+ if (this.isOffline()) {
1035
+ this.debug(`checkFlags offline result: returning empty object`);
1036
+ return {};
1037
+ }
889
1038
  const requestUrl = `${this.apiUrl}/flags/check`;
890
1039
  const requestBody = JSON.stringify(context);
891
1040
  return fetch(requestUrl, {
@@ -903,6 +1052,7 @@ var Schematic = class {
903
1052
  return response.json();
904
1053
  }).then((responseJson) => {
905
1054
  const resp = CheckFlagsResponseFromJSON(responseJson);
1055
+ this.debug(`checkFlags result:`, resp);
906
1056
  return (resp?.data?.flags ?? []).reduce(
907
1057
  (accum, flag) => {
908
1058
  accum[flag.flag] = flag.value;
@@ -921,6 +1071,7 @@ var Schematic = class {
921
1071
  * send an identify event to the Schematic API which will upsert a user and company.
922
1072
  */
923
1073
  identify = (body) => {
1074
+ this.debug(`identify:`, body);
924
1075
  try {
925
1076
  this.setContext({
926
1077
  company: body.company?.keys,
@@ -938,10 +1089,15 @@ var Schematic = class {
938
1089
  * 2. Send the context to the server
939
1090
  * 3. Wait for initial flag values to be returned
940
1091
  * The promise resolves when initial flag values are received.
1092
+ * In offline mode, this will just set the context locally without connecting.
941
1093
  */
942
1094
  setContext = async (context) => {
943
- if (!this.useWebSocket) {
1095
+ if (this.isOffline()) {
944
1096
  this.context = context;
1097
+ this.setIsPending(false);
1098
+ return Promise.resolve();
1099
+ }
1100
+ if (!this.useWebSocket) {
945
1101
  return Promise.resolve();
946
1102
  }
947
1103
  try {
@@ -962,12 +1118,14 @@ var Schematic = class {
962
1118
  */
963
1119
  track = (body) => {
964
1120
  const { company, user, event, traits } = body;
965
- return this.handleEvent("track", {
1121
+ const trackData = {
966
1122
  company: company ?? this.context.company,
967
1123
  event,
968
1124
  traits: traits ?? {},
969
1125
  user: user ?? this.context.user
970
- });
1126
+ };
1127
+ this.debug(`track:`, trackData);
1128
+ return this.handleEvent("track", trackData);
971
1129
  };
972
1130
  /**
973
1131
  * Event processing
@@ -1010,8 +1168,13 @@ var Schematic = class {
1010
1168
  sendEvent = async (event) => {
1011
1169
  const captureUrl = `${this.eventUrl}/e`;
1012
1170
  const payload = JSON.stringify(event);
1171
+ this.debug(`sending event:`, { url: captureUrl, event });
1172
+ if (this.isOffline()) {
1173
+ this.debug(`event not sent (offline mode):`, { event });
1174
+ return Promise.resolve();
1175
+ }
1013
1176
  try {
1014
- await fetch(captureUrl, {
1177
+ const response = await fetch(captureUrl, {
1015
1178
  method: "POST",
1016
1179
  headers: {
1017
1180
  ...this.additionalHeaders ?? {},
@@ -1019,6 +1182,10 @@ var Schematic = class {
1019
1182
  },
1020
1183
  body: payload
1021
1184
  });
1185
+ this.debug(`event sent:`, {
1186
+ status: response.status,
1187
+ statusText: response.statusText
1188
+ });
1022
1189
  } catch (error) {
1023
1190
  console.error("Error sending Schematic event: ", error);
1024
1191
  }
@@ -1033,8 +1200,13 @@ var Schematic = class {
1033
1200
  */
1034
1201
  /**
1035
1202
  * If using websocket mode, close the connection when done.
1203
+ * In offline mode, this is a no-op.
1036
1204
  */
1037
1205
  cleanup = async () => {
1206
+ if (this.isOffline()) {
1207
+ this.debug("cleanup: skipped (offline mode)");
1208
+ return Promise.resolve();
1209
+ }
1038
1210
  if (this.conn) {
1039
1211
  try {
1040
1212
  const socket = await this.conn;
@@ -1048,16 +1220,26 @@ var Schematic = class {
1048
1220
  };
1049
1221
  // Open a websocket connection
1050
1222
  wsConnect = () => {
1223
+ if (this.isOffline()) {
1224
+ this.debug("wsConnect: skipped (offline mode)");
1225
+ return Promise.reject(
1226
+ new Error("WebSocket connection skipped in offline mode")
1227
+ );
1228
+ }
1051
1229
  return new Promise((resolve, reject) => {
1052
1230
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1231
+ this.debug(`connecting to WebSocket:`, wsUrl);
1053
1232
  const webSocket = new WebSocket(wsUrl);
1054
1233
  webSocket.onopen = () => {
1234
+ this.debug(`WebSocket connection opened`);
1055
1235
  resolve(webSocket);
1056
1236
  };
1057
1237
  webSocket.onerror = (error) => {
1238
+ this.debug(`WebSocket connection error:`, error);
1058
1239
  reject(error);
1059
1240
  };
1060
1241
  webSocket.onclose = () => {
1242
+ this.debug(`WebSocket connection closed`);
1061
1243
  this.conn = null;
1062
1244
  };
1063
1245
  });
@@ -1065,21 +1247,37 @@ var Schematic = class {
1065
1247
  // Send a message on the websocket indicating interest in a particular evaluation context
1066
1248
  // and wait for the initial set of flag values to be returned
1067
1249
  wsSendMessage = (socket, context) => {
1250
+ if (this.isOffline()) {
1251
+ this.debug("wsSendMessage: skipped (offline mode)");
1252
+ this.setIsPending(false);
1253
+ return Promise.resolve();
1254
+ }
1068
1255
  return new Promise((resolve, reject) => {
1069
1256
  if (contextString(context) == contextString(this.context)) {
1257
+ this.debug(`WebSocket context unchanged, skipping update`);
1070
1258
  return resolve(this.setIsPending(false));
1071
1259
  }
1260
+ this.debug(`WebSocket context updated:`, context);
1072
1261
  this.context = context;
1073
1262
  const sendMessage = () => {
1074
1263
  let resolved = false;
1075
1264
  const messageHandler = (event) => {
1076
1265
  const message = JSON.parse(event.data);
1266
+ this.debug(`WebSocket message received:`, message);
1077
1267
  if (!(contextString(context) in this.checks)) {
1078
1268
  this.checks[contextString(context)] = {};
1079
1269
  }
1080
1270
  (message.flags ?? []).forEach((flag) => {
1081
1271
  const flagCheck = CheckFlagReturnFromJSON(flag);
1082
1272
  this.checks[contextString(context)][flagCheck.flag] = flagCheck;
1273
+ this.debug(`WebSocket flag update:`, {
1274
+ flag: flagCheck.flag,
1275
+ value: flagCheck.value,
1276
+ flagCheck
1277
+ });
1278
+ if (this.flagCheckListeners[flag.flag]?.size > 0 || this.flagValueListeners[flag.flag]?.size > 0) {
1279
+ this.submitFlagCheckEvent(flagCheck.flag, flagCheck, context);
1280
+ }
1083
1281
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1084
1282
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1085
1283
  });
@@ -1090,19 +1288,23 @@ var Schematic = class {
1090
1288
  }
1091
1289
  };
1092
1290
  socket.addEventListener("message", messageHandler);
1093
- socket.send(
1094
- JSON.stringify({
1095
- apiKey: this.apiKey,
1096
- clientVersion: `schematic-js@${version}`,
1097
- data: context
1098
- })
1099
- );
1291
+ const clientVersion = this.additionalHeaders["X-Schematic-Client-Version"] ?? `schematic-js@${version}`;
1292
+ const messagePayload = {
1293
+ apiKey: this.apiKey,
1294
+ clientVersion,
1295
+ data: context
1296
+ };
1297
+ this.debug(`WebSocket sending message:`, messagePayload);
1298
+ socket.send(JSON.stringify(messagePayload));
1100
1299
  };
1101
1300
  if (socket.readyState === WebSocket.OPEN) {
1301
+ this.debug(`WebSocket already open, sending message`);
1102
1302
  sendMessage();
1103
1303
  } else if (socket.readyState === WebSocket.CONNECTING) {
1304
+ this.debug(`WebSocket connecting, waiting for open to send message`);
1104
1305
  socket.addEventListener("open", sendMessage);
1105
1306
  } else {
1307
+ this.debug(`WebSocket is closed, cannot send message`);
1106
1308
  reject("WebSocket is not open or connecting");
1107
1309
  }
1108
1310
  });
@@ -1159,10 +1361,22 @@ var Schematic = class {
1159
1361
  };
1160
1362
  notifyFlagCheckListeners = (flagKey, check) => {
1161
1363
  const listeners = this.flagCheckListeners?.[flagKey] ?? [];
1364
+ if (listeners.size > 0) {
1365
+ this.debug(
1366
+ `Notifying ${listeners.size} flag check listeners for ${flagKey}`,
1367
+ check
1368
+ );
1369
+ }
1162
1370
  listeners.forEach((listener) => notifyFlagCheckListener(listener, check));
1163
1371
  };
1164
1372
  notifyFlagValueListeners = (flagKey, value) => {
1165
1373
  const listeners = this.flagValueListeners?.[flagKey] ?? [];
1374
+ if (listeners.size > 0) {
1375
+ this.debug(
1376
+ `Notifying ${listeners.size} flag value listeners for ${flagKey}`,
1377
+ { value }
1378
+ );
1379
+ }
1166
1380
  listeners.forEach((listener) => notifyFlagValueListener(listener, value));
1167
1381
  };
1168
1382
  };
@@ -1191,6 +1405,7 @@ export {
1191
1405
  CheckFlagResponseFromJSON,
1192
1406
  CheckFlagReturnFromJSON,
1193
1407
  CheckFlagsResponseFromJSON,
1408
+ EventBodyFlagCheckToJSON,
1194
1409
  RuleType,
1195
1410
  Schematic,
1196
1411
  UsagePeriod
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematichq/schematic-js",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "main": "dist/schematic.cjs.js",
5
5
  "module": "dist/schematic.esm.js",
6
6
  "types": "dist/schematic.d.ts",
@@ -39,7 +39,7 @@
39
39
  "@types/uuid": "^10.0.0",
40
40
  "@typescript-eslint/eslint-plugin": "^8.23.0",
41
41
  "@typescript-eslint/parser": "^8.23.0",
42
- "esbuild": "^0.24.2",
42
+ "esbuild": "^0.25.0",
43
43
  "esbuild-jest": "^0.5.0",
44
44
  "eslint": "^8.57.1",
45
45
  "jest": "^29.7.0",