@schematichq/schematic-js 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
- "use strict";(()=>{var G=Object.create;var I=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty;var ee=(i,r)=>()=>(r||i((r={exports:{}}).exports,r),r.exports);var te=(i,r,n,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of z(r))!Z.call(i,s)&&s!==n&&I(i,s,{get:()=>r[s],enumerable:!(o=X(r,s))||o.enumerable});return i};var re=(i,r,n)=>(n=i!=null?G(Y(i)):{},te(r||!i||!i.__esModule?I(n,"default",{value:i,enumerable:!0}):n,i));var L=ee(F=>{(function(i){var r=function(n){var o=typeof globalThis<"u"&&globalThis||typeof i<"u"&&i||typeof o<"u"&&o,s={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 y(e){return e&&DataView.prototype.isPrototypeOf(e)}if(s.arrayBuffer)var h=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],b=ArrayBuffer.isView||function(e){return e&&h.indexOf(Object.prototype.toString.call(e))>-1};function g(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 S(e){return typeof e!="string"&&(e=String(e)),e}function E(e){var t={next:function(){var a=e.shift();return{done:a===void 0,value:a}}};return s.iterable&&(t[Symbol.iterator]=function(){return t}),t}function d(e){this.map={},e instanceof d?e.forEach(function(t,a){this.append(a,t)},this):Array.isArray(e)?e.forEach(function(t){this.append(t[0],t[1])},this):e&&Object.getOwnPropertyNames(e).forEach(function(t){this.append(t,e[t])},this)}d.prototype.append=function(e,t){e=g(e),t=S(t);var a=this.map[e];this.map[e]=a?a+", "+t:t},d.prototype.delete=function(e){delete this.map[g(e)]},d.prototype.get=function(e){return e=g(e),this.has(e)?this.map[e]:null},d.prototype.has=function(e){return this.map.hasOwnProperty(g(e))},d.prototype.set=function(e,t){this.map[g(e)]=S(t)},d.prototype.forEach=function(e,t){for(var a in this.map)this.map.hasOwnProperty(a)&&e.call(t,this.map[a],a,this)},d.prototype.keys=function(){var e=[];return this.forEach(function(t,a){e.push(a)}),E(e)},d.prototype.values=function(){var e=[];return this.forEach(function(t){e.push(t)}),E(e)},d.prototype.entries=function(){var e=[];return this.forEach(function(t,a){e.push([a,t])}),E(e)},s.iterable&&(d.prototype[Symbol.iterator]=d.prototype.entries);function _(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function R(e){return new Promise(function(t,a){e.onload=function(){t(e.result)},e.onerror=function(){a(e.error)}})}function N(e){var t=new FileReader,a=R(t);return t.readAsArrayBuffer(e),a}function q(e){var t=new FileReader,a=R(t);return t.readAsText(e),a}function M(e){for(var t=new Uint8Array(e),a=new Array(t.length),u=0;u<t.length;u++)a[u]=String.fromCharCode(t[u]);return a.join("")}function C(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function D(){return this.bodyUsed=!1,this._initBody=function(e){this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?typeof e=="string"?this._bodyText=e:s.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:s.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:s.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():s.arrayBuffer&&s.blob&&y(e)?(this._bodyArrayBuffer=C(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):s.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||b(e))?this._bodyArrayBuffer=C(e):this._bodyText=e=Object.prototype.toString.call(e):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):s.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},s.blob&&(this.blob=function(){var e=_(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=_(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 return this.blob().then(N)}),this.text=function(){var e=_(this);if(e)return e;if(this._bodyBlob)return q(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(M(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},s.formData&&(this.formData=function(){return this.text().then(V)}),this.json=function(){return this.text().then(JSON.parse)},this}var W=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function H(e){var t=e.toUpperCase();return W.indexOf(t)>-1?t:e}function v(e,t){if(!(this instanceof v))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');t=t||{};var a=t.body;if(e instanceof v){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,t.headers||(this.headers=new d(e.headers)),this.method=e.method,this.mode=e.mode,this.signal=e.signal,!a&&e._bodyInit!=null&&(a=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=t.credentials||this.credentials||"same-origin",(t.headers||!this.headers)&&(this.headers=new d(t.headers)),this.method=H(t.method||this.method||"GET"),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&a)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(a),(this.method==="GET"||this.method==="HEAD")&&(t.cache==="no-store"||t.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()}}}v.prototype.clone=function(){return new v(this,{body:this._bodyInit})};function V(e){var t=new FormData;return e.trim().split("&").forEach(function(a){if(a){var u=a.split("="),f=u.shift().replace(/\+/g," "),c=u.join("=").replace(/\+/g," ");t.append(decodeURIComponent(f),decodeURIComponent(c))}}),t}function J(e){var t=new d,a=e.replace(/\r?\n[\t ]+/g," ");return a.split("\r").map(function(u){return u.indexOf(`
2
- `)===0?u.substr(1,u.length):u}).forEach(function(u){var f=u.split(":"),c=f.shift().trim();if(c){var T=f.join(":").trim();t.append(c,T)}}),t}D.call(v.prototype);function m(e,t){if(!(this instanceof m))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');t||(t={}),this.type="default",this.status=t.status===void 0?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText=t.statusText===void 0?"":""+t.statusText,this.headers=new d(t.headers),this.url=t.url||"",this._initBody(e)}D.call(m.prototype),m.prototype.clone=function(){return new m(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new d(this.headers),url:this.url})},m.error=function(){var e=new m(null,{status:0,statusText:""});return e.type="error",e};var Q=[301,302,303,307,308];m.redirect=function(e,t){if(Q.indexOf(t)===-1)throw new RangeError("Invalid status code");return new m(null,{status:t,headers:{location:e}})},n.DOMException=o.DOMException;try{new n.DOMException}catch{n.DOMException=function(t,a){this.message=t,this.name=a;var u=Error(t);this.stack=u.stack},n.DOMException.prototype=Object.create(Error.prototype),n.DOMException.prototype.constructor=n.DOMException}function A(e,t){return new Promise(function(a,u){var f=new v(e,t);if(f.signal&&f.signal.aborted)return u(new n.DOMException("Aborted","AbortError"));var c=new XMLHttpRequest;function T(){c.abort()}c.onload=function(){var p={status:c.status,statusText:c.statusText,headers:J(c.getAllResponseHeaders()||"")};p.url="responseURL"in c?c.responseURL:p.headers.get("X-Request-URL");var k="response"in c?c.response:c.responseText;setTimeout(function(){a(new m(k,p))},0)},c.onerror=function(){setTimeout(function(){u(new TypeError("Network request failed"))},0)},c.ontimeout=function(){setTimeout(function(){u(new TypeError("Network request failed"))},0)},c.onabort=function(){setTimeout(function(){u(new n.DOMException("Aborted","AbortError"))},0)};function $(p){try{return p===""&&o.location.href?o.location.href:p}catch{return p}}c.open(f.method,$(f.url),!0),f.credentials==="include"?c.withCredentials=!0:f.credentials==="omit"&&(c.withCredentials=!1),"responseType"in c&&(s.blob?c.responseType="blob":s.arrayBuffer&&f.headers.get("Content-Type")&&f.headers.get("Content-Type").indexOf("application/octet-stream")!==-1&&(c.responseType="arraybuffer")),t&&typeof t.headers=="object"&&!(t.headers instanceof d)?Object.getOwnPropertyNames(t.headers).forEach(function(p){c.setRequestHeader(p,S(t.headers[p]))}):f.headers.forEach(function(p,k){c.setRequestHeader(k,p)}),f.signal&&(f.signal.addEventListener("abort",T),c.onreadystatechange=function(){c.readyState===4&&f.signal.removeEventListener("abort",T)}),c.send(typeof f._bodyInit>"u"?null:f._bodyInit)})}return A.polyfill=!0,o.fetch||(o.fetch=A,o.Headers=d,o.Request=v,o.Response=m),n.Headers=d,n.Request=v,n.Response=m,n.fetch=A,n}({})})(typeof self<"u"?self:F)});var U,ne=new Uint8Array(16);function O(){if(!U&&(U=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!U))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return U(ne)}var l=[];for(let i=0;i<256;++i)l.push((i+256).toString(16).slice(1));function j(i,r=0){return l[i[r+0]]+l[i[r+1]]+l[i[r+2]]+l[i[r+3]]+"-"+l[i[r+4]]+l[i[r+5]]+"-"+l[i[r+6]]+l[i[r+7]]+"-"+l[i[r+8]]+l[i[r+9]]+"-"+l[i[r+10]]+l[i[r+11]]+l[i[r+12]]+l[i[r+13]]+l[i[r+14]]+l[i[r+15]]}var oe=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),P={randomUUID:oe};function ie(i,r,n){if(P.randomUUID&&!r&&!i)return P.randomUUID();i=i||{};let o=i.random||(i.rng||O)();if(o[6]=o[6]&15|64,o[8]=o[8]&63|128,r){n=n||0;for(let s=0;s<16;++s)r[n+s]=o[s];return r}return j(o)}var x=ie;var ve=re(L()),K="schematicId";var B=class{apiKey;apiUrl="https://api.schematichq.com";webSocketUrl="wss://api.schematichq.com";eventUrl="https://c.schematichq.com";conn=null;context={};eventQueue;storage;useWebSocket=!1;values={};flagListener;constructor(r,n){this.apiKey=r,this.eventQueue=[],this.useWebSocket=n?.useWebSocket??!1,this.flagListener=n?.flagListener,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),window?.addEventListener&&window.addEventListener("beforeunload",()=>{this.flushEventQueue()})}async checkFlag(r){let{fallback:n=!1,key:o}=r,s=r.context||this.context;if(this.useWebSocket){let h=this.values[w(s)]??{};return typeof h[o]>"u"?n:h[o]}let y=`${this.apiUrl}/flags/${o}/check`;return fetch(y,{method:"POST",headers:{"X-Schematic-Api-Key":this.apiKey,"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify(s)}).then(h=>{if(!h.ok)throw new Error("Network response was not ok");return h.json()}).then(h=>h.data.value).catch(h=>(console.error("There was a problem with the fetch operation:",h),n))}checkFlags=async r=>{r=r||this.context;let n=`${this.apiUrl}/flags/check`,o=JSON.stringify(r);return fetch(n,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:o}).then(s=>{if(!s.ok)throw new Error("Network response was not ok");return s.json()}).then(s=>(s?.data?.flags??[]).reduce((y,h)=>(y[h.flag]=h.value,y),{})).catch(s=>(console.error("There was a problem with the fetch operation:",s),!1))};cleanup=async()=>{if(this.conn)try{(await this.conn).close()}catch(r){console.error("Error during cleanup:",r)}finally{this.conn=null}};identify=r=>(this.setContext({company:r.company?.keys,user:r.keys}),this.handleEvent("identify",r));setContext=async r=>this.useWebSocket?(this.conn||(this.conn=this.wsConnect()),this.conn.then(n=>this.wsSendMessage(n,r))):(this.context=r,Promise.resolve());track=r=>{let{company:n,user:o,event:s,traits:y}=r;return this.handleEvent("track",{company:n??this.context.company,event:s,traits:y??{},user:o??this.context.user})};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let r=this.eventQueue.shift();r&&this.sendEvent(r)}};getAnonymousId=()=>{if(!this.storage)return x();let r=this.storage.getItem(K);if(typeof r<"u")return r;let n=x();return this.storage.setItem(K,n),n};handleEvent=(r,n)=>{let o={api_key:this.apiKey,body:n,sent_at:new Date().toISOString(),tracker_event_id:x(),tracker_user_id:this.getAnonymousId(),type:r};return document?.hidden?this.storeEvent(o):this.sendEvent(o)};sendEvent=async r=>{let n=`${this.eventUrl}/e`,o=JSON.stringify(r);try{await fetch(n,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8"},body:o})}catch(s){console.error("Error sending Schematic event: ",s)}return Promise.resolve()};storeEvent=r=>(this.eventQueue.push(r),Promise.resolve());wsConnect=()=>new Promise((r,n)=>{let o=`${this.webSocketUrl}/flags/bootstrap`,s=new WebSocket(o);s.onopen=()=>{r(s)},s.onerror=y=>{n(y)},s.onclose=()=>{this.conn=null}});wsSendMessage=(r,n)=>new Promise((o,s)=>{if(w(n)==w(this.context)){o();return}this.context=n;let y=()=>{let h=!1,b=g=>{let S=JSON.parse(g.data);w(n)in this.values||(this.values[w(n)]={}),(S.flags??[]).forEach(E=>{this.values[w(n)][E.flag]=E.value}),this.flagListener&&this.flagListener(this.values[w(n)]),h||(h=!0,o()),r.removeEventListener("message",b)};r.addEventListener("message",b),r.send(JSON.stringify({apiKey:this.apiKey,data:n}))};r.readyState===WebSocket.OPEN?y():r.readyState===WebSocket.CONNECTING?r.addEventListener("open",y):s("WebSocket is not open or connecting")})};function w(i){let r=Object.keys(i).reduce((n,o)=>{let y=Object.keys(i[o]||{}).sort().reduce((h,b)=>(h[b]=i[o][b],h),{});return n[o]=y,n},{});return JSON.stringify(r)}window.Schematic=B;})();
1
+ "use strict";(()=>{var G=Object.create;var C=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,Z=Object.prototype.hasOwnProperty;var ee=(s,t)=>()=>(t||s((t={exports:{}}).exports,t),t.exports);var te=(s,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of z(t))!Z.call(s,o)&&o!==n&&C(s,o,{get:()=>t[o],enumerable:!(i=X(t,o))||i.enumerable});return s};var re=(s,t,n)=>(n=s!=null?G(Y(s)):{},te(t||!s||!s.__esModule?C(n,"default",{value:s,enumerable:!0}):n,s));var j=ee(F=>{(function(s){var t=function(n){var i=typeof globalThis<"u"&&globalThis||typeof s<"u"&&s||typeof i<"u"&&i,o={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 y(e){return e&&DataView.prototype.isPrototypeOf(e)}if(o.arrayBuffer)var f=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],E=ArrayBuffer.isView||function(e){return e&&f.indexOf(Object.prototype.toString.call(e))>-1};function b(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 S(e){return typeof e!="string"&&(e=String(e)),e}function w(e){var r={next:function(){var a=e.shift();return{done:a===void 0,value:a}}};return o.iterable&&(r[Symbol.iterator]=function(){return r}),r}function d(e){this.map={},e instanceof d?e.forEach(function(r,a){this.append(a,r)},this):Array.isArray(e)?e.forEach(function(r){this.append(r[0],r[1])},this):e&&Object.getOwnPropertyNames(e).forEach(function(r){this.append(r,e[r])},this)}d.prototype.append=function(e,r){e=b(e),r=S(r);var a=this.map[e];this.map[e]=a?a+", "+r:r},d.prototype.delete=function(e){delete this.map[b(e)]},d.prototype.get=function(e){return e=b(e),this.has(e)?this.map[e]:null},d.prototype.has=function(e){return this.map.hasOwnProperty(b(e))},d.prototype.set=function(e,r){this.map[b(e)]=S(r)},d.prototype.forEach=function(e,r){for(var a in this.map)this.map.hasOwnProperty(a)&&e.call(r,this.map[a],a,this)},d.prototype.keys=function(){var e=[];return this.forEach(function(r,a){e.push(a)}),w(e)},d.prototype.values=function(){var e=[];return this.forEach(function(r){e.push(r)}),w(e)},d.prototype.entries=function(){var e=[];return this.forEach(function(r,a){e.push([a,r])}),w(e)},o.iterable&&(d.prototype[Symbol.iterator]=d.prototype.entries);function B(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function I(e){return new Promise(function(r,a){e.onload=function(){r(e.result)},e.onerror=function(){a(e.error)}})}function N(e){var r=new FileReader,a=I(r);return r.readAsArrayBuffer(e),a}function q(e){var r=new FileReader,a=I(r);return r.readAsText(e),a}function M(e){for(var r=new Uint8Array(e),a=new Array(r.length),u=0;u<r.length;u++)a[u]=String.fromCharCode(r[u]);return a.join("")}function D(e){if(e.slice)return e.slice(0);var r=new Uint8Array(e.byteLength);return r.set(new Uint8Array(e)),r.buffer}function R(){return this.bodyUsed=!1,this._initBody=function(e){this.bodyUsed=this.bodyUsed,this._bodyInit=e,e?typeof e=="string"?this._bodyText=e:o.blob&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:o.formData&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:o.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():o.arrayBuffer&&o.blob&&y(e)?(this._bodyArrayBuffer=D(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):o.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(e)||E(e))?this._bodyArrayBuffer=D(e):this._bodyText=e=Object.prototype.toString.call(e):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):o.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},o.blob&&(this.blob=function(){var e=B(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=B(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 return this.blob().then(N)}),this.text=function(){var e=B(this);if(e)return e;if(this._bodyBlob)return q(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(M(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},o.formData&&(this.formData=function(){return this.text().then(K)}),this.json=function(){return this.text().then(JSON.parse)},this}var H=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function W(e){var r=e.toUpperCase();return H.indexOf(r)>-1?r:e}function v(e,r){if(!(this instanceof v))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');r=r||{};var a=r.body;if(e instanceof v){if(e.bodyUsed)throw new TypeError("Already read");this.url=e.url,this.credentials=e.credentials,r.headers||(this.headers=new d(e.headers)),this.method=e.method,this.mode=e.mode,this.signal=e.signal,!a&&e._bodyInit!=null&&(a=e._bodyInit,e.bodyUsed=!0)}else this.url=String(e);if(this.credentials=r.credentials||this.credentials||"same-origin",(r.headers||!this.headers)&&(this.headers=new d(r.headers)),this.method=W(r.method||this.method||"GET"),this.mode=r.mode||this.mode||null,this.signal=r.signal||this.signal,this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&a)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(a),(this.method==="GET"||this.method==="HEAD")&&(r.cache==="no-store"||r.cache==="no-cache")){var u=/([?&])_=[^&]*/;if(u.test(this.url))this.url=this.url.replace(u,"$1_="+new Date().getTime());else{var h=/\?/;this.url+=(h.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}v.prototype.clone=function(){return new v(this,{body:this._bodyInit})};function K(e){var r=new FormData;return e.trim().split("&").forEach(function(a){if(a){var u=a.split("="),h=u.shift().replace(/\+/g," "),c=u.join("=").replace(/\+/g," ");r.append(decodeURIComponent(h),decodeURIComponent(c))}}),r}function J(e){var r=new d,a=e.replace(/\r?\n[\t ]+/g," ");return a.split("\r").map(function(u){return u.indexOf(`
2
+ `)===0?u.substr(1,u.length):u}).forEach(function(u){var h=u.split(":"),c=h.shift().trim();if(c){var T=h.join(":").trim();r.append(c,T)}}),r}R.call(v.prototype);function m(e,r){if(!(this instanceof m))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');r||(r={}),this.type="default",this.status=r.status===void 0?200:r.status,this.ok=this.status>=200&&this.status<300,this.statusText=r.statusText===void 0?"":""+r.statusText,this.headers=new d(r.headers),this.url=r.url||"",this._initBody(e)}R.call(m.prototype),m.prototype.clone=function(){return new m(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new d(this.headers),url:this.url})},m.error=function(){var e=new m(null,{status:0,statusText:""});return e.type="error",e};var Q=[301,302,303,307,308];m.redirect=function(e,r){if(Q.indexOf(r)===-1)throw new RangeError("Invalid status code");return new m(null,{status:r,headers:{location:e}})},n.DOMException=i.DOMException;try{new n.DOMException}catch{n.DOMException=function(r,a){this.message=r,this.name=a;var u=Error(r);this.stack=u.stack},n.DOMException.prototype=Object.create(Error.prototype),n.DOMException.prototype.constructor=n.DOMException}function O(e,r){return new Promise(function(a,u){var h=new v(e,r);if(h.signal&&h.signal.aborted)return u(new n.DOMException("Aborted","AbortError"));var c=new XMLHttpRequest;function T(){c.abort()}c.onload=function(){var p={status:c.status,statusText:c.statusText,headers:J(c.getAllResponseHeaders()||"")};p.url="responseURL"in c?c.responseURL:p.headers.get("X-Request-URL");var P="response"in c?c.response:c.responseText;setTimeout(function(){a(new m(P,p))},0)},c.onerror=function(){setTimeout(function(){u(new TypeError("Network request failed"))},0)},c.ontimeout=function(){setTimeout(function(){u(new TypeError("Network request failed"))},0)},c.onabort=function(){setTimeout(function(){u(new n.DOMException("Aborted","AbortError"))},0)};function $(p){try{return p===""&&i.location.href?i.location.href:p}catch{return p}}c.open(h.method,$(h.url),!0),h.credentials==="include"?c.withCredentials=!0:h.credentials==="omit"&&(c.withCredentials=!1),"responseType"in c&&(o.blob?c.responseType="blob":o.arrayBuffer&&h.headers.get("Content-Type")&&h.headers.get("Content-Type").indexOf("application/octet-stream")!==-1&&(c.responseType="arraybuffer")),r&&typeof r.headers=="object"&&!(r.headers instanceof d)?Object.getOwnPropertyNames(r.headers).forEach(function(p){c.setRequestHeader(p,S(r.headers[p]))}):h.headers.forEach(function(p,P){c.setRequestHeader(P,p)}),h.signal&&(h.signal.addEventListener("abort",T),c.onreadystatechange=function(){c.readyState===4&&h.signal.removeEventListener("abort",T)}),c.send(typeof h._bodyInit>"u"?null:h._bodyInit)})}return O.polyfill=!0,i.fetch||(i.fetch=O,i.Headers=d,i.Request=v,i.Response=m),n.Headers=d,n.Request=v,n.Response=m,n.fetch=O,n}({})})(typeof self<"u"?self:F)});var U,ne=new Uint8Array(16);function k(){if(!U&&(U=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!U))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return U(ne)}var l=[];for(let s=0;s<256;++s)l.push((s+256).toString(16).slice(1));function L(s,t=0){return l[s[t+0]]+l[s[t+1]]+l[s[t+2]]+l[s[t+3]]+"-"+l[s[t+4]]+l[s[t+5]]+"-"+l[s[t+6]]+l[s[t+7]]+"-"+l[s[t+8]]+l[s[t+9]]+"-"+l[s[t+10]]+l[s[t+11]]+l[s[t+12]]+l[s[t+13]]+l[s[t+14]]+l[s[t+15]]}var ie=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),_={randomUUID:ie};function se(s,t,n){if(_.randomUUID&&!t&&!s)return _.randomUUID();s=s||{};let i=s.random||(s.rng||k)();if(i[6]=i[6]&15|64,i[8]=i[8]&63|128,t){n=n||0;for(let o=0;o<16;++o)t[n+o]=i[o];return t}return L(i)}var x=se;var ge=re(j());function g(s){let t=Object.keys(s).reduce((n,i)=>{let y=Object.keys(s[i]||{}).sort().reduce((f,E)=>(f[E]=s[i][E],f),{});return n[i]=y,n},{});return JSON.stringify(t)}var V="schematicId";var A=class{apiKey;apiUrl="https://api.schematichq.com";conn=null;context={};eventQueue;eventUrl="https://c.schematichq.com";flagListener;flagValueListeners={};isPending=!0;isPendingListeners=new Set;storage;useWebSocket=!1;values={};webSocketUrl="wss://api.schematichq.com";constructor(t,n){this.apiKey=t,this.eventQueue=[],this.useWebSocket=n?.useWebSocket??!1,this.flagListener=n?.flagListener,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:i}=t,o=t.context||this.context;if(this.useWebSocket){let f=this.values[g(o)]??{};return typeof f[i]>"u"?n:f[i]}let y=`${this.apiUrl}/flags/${i}/check`;return fetch(y,{method:"POST",headers:{"X-Schematic-Api-Key":this.apiKey,"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify(o)}).then(f=>{if(!f.ok)throw new Error("Network response was not ok");return f.json()}).then(f=>f.data.value).catch(f=>(console.error("There was a problem with the fetch operation:",f),n))}checkFlags=async t=>{t=t||this.context;let n=`${this.apiUrl}/flags/check`,i=JSON.stringify(t);return fetch(n,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:i}).then(o=>{if(!o.ok)throw new Error("Network response was not ok");return o.json()}).then(o=>(o?.data?.flags??[]).reduce((y,f)=>(y[f.flag]=f.value,y),{})).catch(o=>(console.error("There was a problem with the fetch operation:",o),!1))};identify=t=>(this.setContext({company:t.company?.keys,user:t.keys}),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){console.error("Error setting Schematic context:",n)}};track=t=>{let{company:n,user:i,event:o,traits:y}=t;return this.handleEvent("track",{company:n??this.context.company,event:o,traits:y??{},user:i??this.context.user})};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let t=this.eventQueue.shift();t&&this.sendEvent(t)}};getAnonymousId=()=>{if(!this.storage)return x();let t=this.storage.getItem(V);if(typeof t<"u")return t;let n=x();return this.storage.setItem(V,n),n};handleEvent=(t,n)=>{let i={api_key:this.apiKey,body:n,sent_at:new Date().toISOString(),tracker_event_id:x(),tracker_user_id:this.getAnonymousId(),type:t};return document?.hidden?this.storeEvent(i):this.sendEvent(i)};sendEvent=async t=>{let n=`${this.eventUrl}/e`,i=JSON.stringify(t);try{await fetch(n,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8"},body:i})}catch(o){console.error("Error sending Schematic event: ",o)}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 i=`${this.webSocketUrl}/flags/bootstrap`,o=new WebSocket(i);o.onopen=()=>{t(o)},o.onerror=y=>{n(y)},o.onclose=()=>{this.conn=null}});wsSendMessage=(t,n)=>new Promise((i,o)=>{if(g(n)==g(this.context)){i();return}this.context=n;let y=()=>{let f=!1,E=b=>{let S=JSON.parse(b.data);g(n)in this.values||(this.values[g(n)]={}),(S.flags??[]).forEach(w=>{this.values[g(n)][w.flag]=w.value,this.notifyFlagValueListeners(w.flag)}),this.flagListener&&this.flagListener(this.getFlagValues()),this.setIsPending(!1),f||(f=!0,i())};t.addEventListener("message",E),t.send(JSON.stringify({apiKey:this.apiKey,data:n}))};t.readyState===WebSocket.OPEN?y():t.readyState===WebSocket.CONNECTING?t.addEventListener("open",y):o("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=>n())};getFlagValue=t=>this.getFlagValues()[t];getFlagValues=()=>{let t=g(this.context);return this.values[t]??{}};addFlagValueListener=(t,n)=>(t in this.flagValueListeners||(this.flagValueListeners[t]=new Set),this.flagValueListeners[t].add(n),()=>{this.flagValueListeners[t].delete(n)});notifyFlagValueListeners=t=>{(this.flagValueListeners?.[t]??[]).forEach(i=>i())}};window.Schematic=A;})();
3
3
  /* @preserve */
@@ -589,19 +589,40 @@ var v4_default = v4;
589
589
 
590
590
  // src/index.ts
591
591
  var import_polyfill = __toESM(require_browser_polyfill());
592
+
593
+ // src/utils.ts
594
+ function contextString(context) {
595
+ const sortedContext = Object.keys(context).reduce((acc, key) => {
596
+ const sortedKeys = Object.keys(
597
+ context[key] || {}
598
+ ).sort();
599
+ const sortedObj = sortedKeys.reduce((obj, sortedKey) => {
600
+ obj[sortedKey] = context[key][sortedKey];
601
+ return obj;
602
+ }, {});
603
+ acc[key] = sortedObj;
604
+ return acc;
605
+ }, {});
606
+ return JSON.stringify(sortedContext);
607
+ }
608
+
609
+ // src/index.ts
592
610
  var anonymousIdKey = "schematicId";
593
611
  var Schematic = class {
594
612
  apiKey;
595
613
  apiUrl = "https://api.schematichq.com";
596
- webSocketUrl = "wss://api.schematichq.com";
597
- eventUrl = "https://c.schematichq.com";
598
614
  conn = null;
599
615
  context = {};
600
616
  eventQueue;
617
+ eventUrl = "https://c.schematichq.com";
618
+ flagListener;
619
+ flagValueListeners = {};
620
+ isPending = true;
621
+ isPendingListeners = /* @__PURE__ */ new Set();
601
622
  storage;
602
623
  useWebSocket = false;
603
624
  values = {};
604
- flagListener;
625
+ webSocketUrl = "wss://api.schematichq.com";
605
626
  constructor(apiKey, options) {
606
627
  this.apiKey = apiKey;
607
628
  this.eventQueue = [];
@@ -621,12 +642,14 @@ var Schematic = class {
621
642
  if (options?.webSocketUrl !== void 0) {
622
643
  this.webSocketUrl = options.webSocketUrl;
623
644
  }
624
- if (window?.addEventListener) {
645
+ if (typeof window !== "undefined" && window?.addEventListener) {
625
646
  window.addEventListener("beforeunload", () => {
626
647
  this.flushEventQueue();
627
648
  });
628
649
  }
629
650
  }
651
+ // Get value for a single flag
652
+ // If in websocket mode, return the local value, otherwise make an API call
630
653
  async checkFlag(options) {
631
654
  const { fallback = false, key } = options;
632
655
  const context = options.context || this.context;
@@ -654,7 +677,7 @@ var Schematic = class {
654
677
  return fallback;
655
678
  });
656
679
  }
657
- // Make a REST API call to fetch all flag values for a given context
680
+ // Make an API call to fetch all flag values for a given context (use if not in websocket mode)
658
681
  checkFlags = async (context) => {
659
682
  context = context || this.context;
660
683
  const requestUrl = `${this.apiUrl}/flags/check`;
@@ -684,18 +707,6 @@ var Schematic = class {
684
707
  return false;
685
708
  });
686
709
  };
687
- cleanup = async () => {
688
- if (this.conn) {
689
- try {
690
- const socket = await this.conn;
691
- socket.close();
692
- } catch (error) {
693
- console.error("Error during cleanup:", error);
694
- } finally {
695
- this.conn = null;
696
- }
697
- }
698
- };
699
710
  // Send an identify event
700
711
  identify = (body) => {
701
712
  this.setContext({
@@ -713,12 +724,16 @@ var Schematic = class {
713
724
  this.context = context;
714
725
  return Promise.resolve();
715
726
  }
716
- if (!this.conn) {
717
- this.conn = this.wsConnect();
727
+ try {
728
+ this.setIsPending(true);
729
+ if (!this.conn) {
730
+ this.conn = this.wsConnect();
731
+ }
732
+ const socket = await this.conn;
733
+ await this.wsSendMessage(socket, context);
734
+ } catch (error) {
735
+ console.error("Error setting Schematic context:", error);
718
736
  }
719
- return this.conn.then((socket) => {
720
- return this.wsSendMessage(socket, context);
721
- });
722
737
  };
723
738
  // Send track event
724
739
  track = (body) => {
@@ -730,6 +745,9 @@ var Schematic = class {
730
745
  user: user ?? this.context.user
731
746
  });
732
747
  };
748
+ /**
749
+ * Event processing
750
+ */
733
751
  flushEventQueue = () => {
734
752
  while (this.eventQueue.length > 0) {
735
753
  const event = this.eventQueue.shift();
@@ -785,6 +803,22 @@ var Schematic = class {
785
803
  this.eventQueue.push(event);
786
804
  return Promise.resolve();
787
805
  };
806
+ /**
807
+ * Websocket management
808
+ */
809
+ cleanup = async () => {
810
+ if (this.conn) {
811
+ try {
812
+ const socket = await this.conn;
813
+ socket.close();
814
+ } catch (error) {
815
+ console.error("Error during cleanup:", error);
816
+ } finally {
817
+ this.conn = null;
818
+ }
819
+ }
820
+ };
821
+ // Open a websocket connection
788
822
  wsConnect = () => {
789
823
  return new Promise((resolve, reject) => {
790
824
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap`;
@@ -800,6 +834,8 @@ var Schematic = class {
800
834
  };
801
835
  });
802
836
  };
837
+ // Send a message on the websocket indicating interest in a particular evaluation context
838
+ // and wait for the initial set of flag values to be returned
803
839
  wsSendMessage = (socket, context) => {
804
840
  return new Promise((resolve, reject) => {
805
841
  if (contextString(context) == contextString(this.context)) {
@@ -817,16 +853,17 @@ var Schematic = class {
817
853
  (message.flags ?? []).forEach(
818
854
  (flag) => {
819
855
  this.values[contextString(context)][flag.flag] = flag.value;
856
+ this.notifyFlagValueListeners(flag.flag);
820
857
  }
821
858
  );
822
859
  if (this.flagListener) {
823
- this.flagListener(this.values[contextString(context)]);
860
+ this.flagListener(this.getFlagValues());
824
861
  }
862
+ this.setIsPending(false);
825
863
  if (!resolved) {
826
864
  resolved = true;
827
865
  resolve();
828
866
  }
829
- socket.removeEventListener("message", messageHandler);
830
867
  };
831
868
  socket.addEventListener("message", messageHandler);
832
869
  socket.send(
@@ -845,19 +882,44 @@ var Schematic = class {
845
882
  }
846
883
  });
847
884
  };
885
+ /**
886
+ * State management
887
+ */
888
+ // isPending state
889
+ getIsPending = () => {
890
+ return this.isPending;
891
+ };
892
+ addIsPendingListener = (listener) => {
893
+ this.isPendingListeners.add(listener);
894
+ return () => {
895
+ this.isPendingListeners.delete(listener);
896
+ };
897
+ };
898
+ setIsPending = (isPending) => {
899
+ this.isPending = isPending;
900
+ this.isPendingListeners.forEach((listener) => listener());
901
+ };
902
+ // flagValues state
903
+ getFlagValue = (flagKey) => {
904
+ const values = this.getFlagValues();
905
+ return values[flagKey];
906
+ };
907
+ getFlagValues = () => {
908
+ const contextStr = contextString(this.context);
909
+ return this.values[contextStr] ?? {};
910
+ };
911
+ addFlagValueListener = (flagKey, listener) => {
912
+ if (!(flagKey in this.flagValueListeners)) {
913
+ this.flagValueListeners[flagKey] = /* @__PURE__ */ new Set();
914
+ }
915
+ this.flagValueListeners[flagKey].add(listener);
916
+ return () => {
917
+ this.flagValueListeners[flagKey].delete(listener);
918
+ };
919
+ };
920
+ notifyFlagValueListeners = (flagKey) => {
921
+ const listeners = this.flagValueListeners?.[flagKey] ?? [];
922
+ listeners.forEach((listener) => listener());
923
+ };
848
924
  };
849
- function contextString(context) {
850
- const sortedContext = Object.keys(context).reduce((acc, key) => {
851
- const sortedKeys = Object.keys(
852
- context[key] || {}
853
- ).sort();
854
- const sortedObj = sortedKeys.reduce((obj, sortedKey) => {
855
- obj[sortedKey] = context[key][sortedKey];
856
- return obj;
857
- }, {});
858
- acc[key] = sortedObj;
859
- return acc;
860
- }, {});
861
- return JSON.stringify(sortedContext);
862
- }
863
925
  /* @preserve */
@@ -52,29 +52,48 @@ export declare type Keys = Record<string, string>;
52
52
  export declare class Schematic {
53
53
  private apiKey;
54
54
  private apiUrl;
55
- private webSocketUrl;
56
- private eventUrl;
57
55
  private conn;
58
56
  private context;
59
57
  private eventQueue;
58
+ private eventUrl;
59
+ private flagListener?;
60
+ private flagValueListeners;
61
+ private isPending;
62
+ private isPendingListeners;
60
63
  private storage;
61
64
  private useWebSocket;
62
65
  private values;
63
- private flagListener?;
66
+ private webSocketUrl;
64
67
  constructor(apiKey: string, options?: SchematicOptions);
65
68
  checkFlag(options: CheckOptions): Promise<boolean>;
66
69
  checkFlags: (context?: SchematicContext) => Promise<Record<string, boolean>>;
67
- cleanup: () => Promise<void>;
68
70
  identify: (body: EventBodyIdentify) => Promise<void>;
69
71
  setContext: (context: SchematicContext) => Promise<void>;
70
72
  track: (body: EventBodyTrack) => Promise<void>;
73
+ /**
74
+ * Event processing
75
+ */
71
76
  private flushEventQueue;
72
77
  private getAnonymousId;
73
78
  private handleEvent;
74
79
  private sendEvent;
75
80
  private storeEvent;
81
+ /**
82
+ * Websocket management
83
+ */
84
+ cleanup: () => Promise<void>;
76
85
  private wsConnect;
77
86
  private wsSendMessage;
87
+ /**
88
+ * State management
89
+ */
90
+ getIsPending: () => boolean;
91
+ addIsPendingListener: (listener: () => void) => () => void;
92
+ private setIsPending;
93
+ getFlagValue: (flagKey: string) => boolean;
94
+ getFlagValues: () => Record<string, boolean>;
95
+ addFlagValueListener: (flagKey: string, listener: () => void) => () => void;
96
+ private notifyFlagValueListeners;
78
97
  }
79
98
 
80
99
  export declare type SchematicContext = {
@@ -576,19 +576,40 @@ var v4_default = v4;
576
576
 
577
577
  // src/index.ts
578
578
  var import_polyfill = __toESM(require_browser_polyfill());
579
+
580
+ // src/utils.ts
581
+ function contextString(context) {
582
+ const sortedContext = Object.keys(context).reduce((acc, key) => {
583
+ const sortedKeys = Object.keys(
584
+ context[key] || {}
585
+ ).sort();
586
+ const sortedObj = sortedKeys.reduce((obj, sortedKey) => {
587
+ obj[sortedKey] = context[key][sortedKey];
588
+ return obj;
589
+ }, {});
590
+ acc[key] = sortedObj;
591
+ return acc;
592
+ }, {});
593
+ return JSON.stringify(sortedContext);
594
+ }
595
+
596
+ // src/index.ts
579
597
  var anonymousIdKey = "schematicId";
580
598
  var Schematic = class {
581
599
  apiKey;
582
600
  apiUrl = "https://api.schematichq.com";
583
- webSocketUrl = "wss://api.schematichq.com";
584
- eventUrl = "https://c.schematichq.com";
585
601
  conn = null;
586
602
  context = {};
587
603
  eventQueue;
604
+ eventUrl = "https://c.schematichq.com";
605
+ flagListener;
606
+ flagValueListeners = {};
607
+ isPending = true;
608
+ isPendingListeners = /* @__PURE__ */ new Set();
588
609
  storage;
589
610
  useWebSocket = false;
590
611
  values = {};
591
- flagListener;
612
+ webSocketUrl = "wss://api.schematichq.com";
592
613
  constructor(apiKey, options) {
593
614
  this.apiKey = apiKey;
594
615
  this.eventQueue = [];
@@ -608,12 +629,14 @@ var Schematic = class {
608
629
  if (options?.webSocketUrl !== void 0) {
609
630
  this.webSocketUrl = options.webSocketUrl;
610
631
  }
611
- if (window?.addEventListener) {
632
+ if (typeof window !== "undefined" && window?.addEventListener) {
612
633
  window.addEventListener("beforeunload", () => {
613
634
  this.flushEventQueue();
614
635
  });
615
636
  }
616
637
  }
638
+ // Get value for a single flag
639
+ // If in websocket mode, return the local value, otherwise make an API call
617
640
  async checkFlag(options) {
618
641
  const { fallback = false, key } = options;
619
642
  const context = options.context || this.context;
@@ -641,7 +664,7 @@ var Schematic = class {
641
664
  return fallback;
642
665
  });
643
666
  }
644
- // Make a REST API call to fetch all flag values for a given context
667
+ // Make an API call to fetch all flag values for a given context (use if not in websocket mode)
645
668
  checkFlags = async (context) => {
646
669
  context = context || this.context;
647
670
  const requestUrl = `${this.apiUrl}/flags/check`;
@@ -671,18 +694,6 @@ var Schematic = class {
671
694
  return false;
672
695
  });
673
696
  };
674
- cleanup = async () => {
675
- if (this.conn) {
676
- try {
677
- const socket = await this.conn;
678
- socket.close();
679
- } catch (error) {
680
- console.error("Error during cleanup:", error);
681
- } finally {
682
- this.conn = null;
683
- }
684
- }
685
- };
686
697
  // Send an identify event
687
698
  identify = (body) => {
688
699
  this.setContext({
@@ -700,12 +711,16 @@ var Schematic = class {
700
711
  this.context = context;
701
712
  return Promise.resolve();
702
713
  }
703
- if (!this.conn) {
704
- this.conn = this.wsConnect();
714
+ try {
715
+ this.setIsPending(true);
716
+ if (!this.conn) {
717
+ this.conn = this.wsConnect();
718
+ }
719
+ const socket = await this.conn;
720
+ await this.wsSendMessage(socket, context);
721
+ } catch (error) {
722
+ console.error("Error setting Schematic context:", error);
705
723
  }
706
- return this.conn.then((socket) => {
707
- return this.wsSendMessage(socket, context);
708
- });
709
724
  };
710
725
  // Send track event
711
726
  track = (body) => {
@@ -717,6 +732,9 @@ var Schematic = class {
717
732
  user: user ?? this.context.user
718
733
  });
719
734
  };
735
+ /**
736
+ * Event processing
737
+ */
720
738
  flushEventQueue = () => {
721
739
  while (this.eventQueue.length > 0) {
722
740
  const event = this.eventQueue.shift();
@@ -772,6 +790,22 @@ var Schematic = class {
772
790
  this.eventQueue.push(event);
773
791
  return Promise.resolve();
774
792
  };
793
+ /**
794
+ * Websocket management
795
+ */
796
+ cleanup = async () => {
797
+ if (this.conn) {
798
+ try {
799
+ const socket = await this.conn;
800
+ socket.close();
801
+ } catch (error) {
802
+ console.error("Error during cleanup:", error);
803
+ } finally {
804
+ this.conn = null;
805
+ }
806
+ }
807
+ };
808
+ // Open a websocket connection
775
809
  wsConnect = () => {
776
810
  return new Promise((resolve, reject) => {
777
811
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap`;
@@ -787,6 +821,8 @@ var Schematic = class {
787
821
  };
788
822
  });
789
823
  };
824
+ // Send a message on the websocket indicating interest in a particular evaluation context
825
+ // and wait for the initial set of flag values to be returned
790
826
  wsSendMessage = (socket, context) => {
791
827
  return new Promise((resolve, reject) => {
792
828
  if (contextString(context) == contextString(this.context)) {
@@ -804,16 +840,17 @@ var Schematic = class {
804
840
  (message.flags ?? []).forEach(
805
841
  (flag) => {
806
842
  this.values[contextString(context)][flag.flag] = flag.value;
843
+ this.notifyFlagValueListeners(flag.flag);
807
844
  }
808
845
  );
809
846
  if (this.flagListener) {
810
- this.flagListener(this.values[contextString(context)]);
847
+ this.flagListener(this.getFlagValues());
811
848
  }
849
+ this.setIsPending(false);
812
850
  if (!resolved) {
813
851
  resolved = true;
814
852
  resolve();
815
853
  }
816
- socket.removeEventListener("message", messageHandler);
817
854
  };
818
855
  socket.addEventListener("message", messageHandler);
819
856
  socket.send(
@@ -832,21 +869,46 @@ var Schematic = class {
832
869
  }
833
870
  });
834
871
  };
872
+ /**
873
+ * State management
874
+ */
875
+ // isPending state
876
+ getIsPending = () => {
877
+ return this.isPending;
878
+ };
879
+ addIsPendingListener = (listener) => {
880
+ this.isPendingListeners.add(listener);
881
+ return () => {
882
+ this.isPendingListeners.delete(listener);
883
+ };
884
+ };
885
+ setIsPending = (isPending) => {
886
+ this.isPending = isPending;
887
+ this.isPendingListeners.forEach((listener) => listener());
888
+ };
889
+ // flagValues state
890
+ getFlagValue = (flagKey) => {
891
+ const values = this.getFlagValues();
892
+ return values[flagKey];
893
+ };
894
+ getFlagValues = () => {
895
+ const contextStr = contextString(this.context);
896
+ return this.values[contextStr] ?? {};
897
+ };
898
+ addFlagValueListener = (flagKey, listener) => {
899
+ if (!(flagKey in this.flagValueListeners)) {
900
+ this.flagValueListeners[flagKey] = /* @__PURE__ */ new Set();
901
+ }
902
+ this.flagValueListeners[flagKey].add(listener);
903
+ return () => {
904
+ this.flagValueListeners[flagKey].delete(listener);
905
+ };
906
+ };
907
+ notifyFlagValueListeners = (flagKey) => {
908
+ const listeners = this.flagValueListeners?.[flagKey] ?? [];
909
+ listeners.forEach((listener) => listener());
910
+ };
835
911
  };
836
- function contextString(context) {
837
- const sortedContext = Object.keys(context).reduce((acc, key) => {
838
- const sortedKeys = Object.keys(
839
- context[key] || {}
840
- ).sort();
841
- const sortedObj = sortedKeys.reduce((obj, sortedKey) => {
842
- obj[sortedKey] = context[key][sortedKey];
843
- return obj;
844
- }, {});
845
- acc[key] = sortedObj;
846
- return acc;
847
- }, {});
848
- return JSON.stringify(sortedContext);
849
- }
850
912
  export {
851
913
  Schematic
852
914
  };
package/package.json CHANGED
@@ -47,6 +47,6 @@
47
47
  "test": "jest --config jest.config.js"
48
48
  },
49
49
  "types": "dist/schematic.d.ts",
50
- "version": "0.1.13",
50
+ "version": "0.1.14",
51
51
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
52
52
  }