@schematichq/schematic-js 1.2.3 → 1.2.5
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/dist/schematic.browser.js +2 -2
- package/dist/schematic.cjs.js +74 -15
- package/dist/schematic.d.ts +5 -2
- package/dist/schematic.esm.js +74 -15
- package/package.json +12 -9
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";(()=>{var
|
|
2
|
-
`)===0?h.substr(1,h.length):h}).forEach(function(h){var p=h.split(":"),c=p.shift().trim();if(c){var U=p.join(":").trim();try{i.append(c,U)}catch(A){console.warn("Response "+A.message)}}}),i}H.call(R.prototype);function E(t,i){if(!(this instanceof E))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(i||(i={}),this.type="default",this.status=i.status===void 0?200:i.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=i.statusText===void 0?"":""+i.statusText,this.headers=new g(i.headers),this.url=i.url||"",this._initBody(t)}H.call(E.prototype),E.prototype.clone=function(){return new E(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new g(this.headers),url:this.url})},E.error=function(){var t=new E(null,{status:200,statusText:""});return t.ok=!1,t.status=0,t.type="error",t};var re=[301,302,303,307,308];E.redirect=function(t,i){if(re.indexOf(i)===-1)throw new RangeError("Invalid status code");return new E(null,{status:i,headers:{location:t}})},r.DOMException=s.DOMException;try{new r.DOMException}catch{r.DOMException=function(i,l){this.message=i,this.name=l;var h=Error(i);this.stack=h.stack},r.DOMException.prototype=Object.create(Error.prototype),r.DOMException.prototype.constructor=r.DOMException}function I(t,i){return new Promise(function(l,h){var p=new R(t,i);if(p.signal&&p.signal.aborted)return h(new r.DOMException("Aborted","AbortError"));var c=new XMLHttpRequest;function U(){c.abort()}c.onload=function(){var b={statusText:c.statusText,headers:te(c.getAllResponseHeaders()||"")};p.url.indexOf("file://")===0&&(c.status<200||c.status>599)?b.status=200:b.status=c.status,b.url="responseURL"in c?c.responseURL:b.headers.get("X-Request-URL");var w="response"in c?c.response:c.responseText;setTimeout(function(){l(new E(w,b))},0)},c.onerror=function(){setTimeout(function(){h(new TypeError("Network request failed"))},0)},c.ontimeout=function(){setTimeout(function(){h(new TypeError("Network request timed out"))},0)},c.onabort=function(){setTimeout(function(){h(new r.DOMException("Aborted","AbortError"))},0)};function A(b){try{return b===""&&s.location.href?s.location.href:b}catch{return b}}if(c.open(p.method,A(p.url),!0),p.credentials==="include"?c.withCredentials=!0:p.credentials==="omit"&&(c.withCredentials=!1),"responseType"in c&&(a.blob?c.responseType="blob":a.arrayBuffer&&(c.responseType="arraybuffer")),i&&typeof i.headers=="object"&&!(i.headers instanceof g||s.Headers&&i.headers instanceof s.Headers)){var W=[];Object.getOwnPropertyNames(i.headers).forEach(function(b){W.push(d(b)),c.setRequestHeader(b,y(i.headers[b]))}),p.headers.forEach(function(b,w){W.indexOf(w)===-1&&c.setRequestHeader(w,b)})}else p.headers.forEach(function(b,w){c.setRequestHeader(w,b)});p.signal&&(p.signal.addEventListener("abort",U),c.onreadystatechange=function(){c.readyState===4&&p.signal.removeEventListener("abort",U)}),c.send(typeof p._bodyInit>"u"?null:p._bodyInit)})}return I.polyfill=!0,s.fetch||(s.fetch=I,s.Headers=g,s.Request=R,s.Response=E),r.Headers=g,r.Request=R,r.Response=E,r.fetch=I,r}({})})(typeof self<"u"?self:z)});var k=[];for(let n=0;n<256;++n)k.push((n+256).toString(16).slice(1));function K(n,e=0){return(k[n[e+0]]+k[n[e+1]]+k[n[e+2]]+k[n[e+3]]+"-"+k[n[e+4]]+k[n[e+5]]+"-"+k[n[e+6]]+k[n[e+7]]+"-"+k[n[e+8]]+k[n[e+9]]+"-"+k[n[e+10]]+k[n[e+11]]+k[n[e+12]]+k[n[e+13]]+k[n[e+14]]+k[n[e+15]]).toLowerCase()}var P,fe=new Uint8Array(16);function L(){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(fe)}var de=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),B={randomUUID:de};function he(n,e,r){if(B.randomUUID&&!e&&!n)return B.randomUUID();n=n||{};let s=n.random??n.rng?.()??L();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,e){if(r=r||0,r<0||r+16>e.length)throw new RangeError(`UUID byte range ${r}:${r+15} is out of buffer bounds`);for(let a=0;a<16;++a)e[r+a]=s[a];return e}return K(s)}var _=he;var rt=ce(X());function x(n){return pe(n,!1)}function pe(n,e){return n==null?n:{companyId:n.company_id==null?void 0:n.company_id,error:n.error==null?void 0:n.error,featureAllocation:n.feature_allocation==null?void 0:n.feature_allocation,featureUsage:n.feature_usage==null?void 0:n.feature_usage,featureUsageEvent:n.feature_usage_event==null?void 0:n.feature_usage_event,featureUsagePeriod:n.feature_usage_period==null?void 0:n.feature_usage_period,featureUsageResetAt:n.feature_usage_reset_at==null?void 0:new Date(n.feature_usage_reset_at),flag:n.flag,flagId:n.flag_id==null?void 0:n.flag_id,reason:n.reason,ruleId:n.rule_id==null?void 0:n.rule_id,ruleType:n.rule_type==null?void 0:n.rule_type,userId:n.user_id==null?void 0:n.user_id,value:n.value}}function N(n){return ye(n,!1)}function ye(n,e=!1){return n==null?n:{company_id:n.companyId,error:n.error,flag_id:n.flagId,flag_key:n.flagKey,reason:n.reason,req_company:n.reqCompany,req_user:n.reqUser,rule_id:n.ruleId,user_id:n.userId,value:n.value}}function T(n){return be(n,!1)}function be(n,e){return n==null?n:{data:x(n.data),params:n.params}}function G(n){return ke(n,!1)}function ke(n,e){return n==null?n:{flags:n.flags.map(x)}}function J(n){return ve(n,!1)}function ve(n,e){return n==null?n:{data:G(n.data),params:n.params}}var O=n=>{let{companyId:e,error:r,featureAllocation:s,featureUsage:a,featureUsageEvent:f,featureUsagePeriod:o,featureUsageResetAt:u,flag:d,flagId:y,reason:F,ruleId:g,ruleType:v,userId:m,value:S}=x(n);return{featureUsageExceeded:!S&&(v=="company_override_usage_exceeded"||v=="plan_entitlement_usage_exceeded"),companyId:e??void 0,error:r??void 0,featureAllocation:s??void 0,featureUsage:a??void 0,featureUsageEvent:f===null?void 0:f,featureUsagePeriod:o??void 0,featureUsageResetAt:u??void 0,flag:d,flagId:y??void 0,reason:F,ruleId:g??void 0,ruleType:v??void 0,userId:m??void 0,value:S}};function C(n){let e=Object.keys(n).reduce((r,s)=>{let f=Object.keys(n[s]||{}).sort().reduce((o,u)=>(o[u]=n[s][u],o),{});return r[s]=f,r},{});return JSON.stringify(e)}var M="1.2.3";var Q="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={};featureUsageEventMap={};webSocketUrl="wss://api.schematichq.com";constructor(e,r){if(this.apiKey=e,this.eventQueue=[],this.useWebSocket=r?.useWebSocket??!1,this.debugEnabled=r?.debug??!1,this.offlineEnabled=r?.offline??!1,typeof window<"u"&&typeof window.location<"u"){let s=new URLSearchParams(window.location.search),a=s.get("schematic_debug");a!==null&&(a===""||a==="true"||a==="1")&&(this.debugEnabled=!0);let f=s.get("schematic_offline");f!==null&&(f===""||f==="true"||f==="1")&&(this.offlineEnabled=!0,this.debugEnabled=!0)}this.offlineEnabled&&r?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${M}`,...r?.additionalHeaders??{}},r?.storage?this.storage=r.storage:typeof localStorage<"u"&&(this.storage=localStorage),r?.apiUrl!==void 0&&(this.apiUrl=r.apiUrl),r?.eventUrl!==void 0&&(this.eventUrl=r.eventUrl),r?.webSocketUrl!==void 0&&(this.webSocketUrl=r.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(e){let{fallback:r=!1,key:s}=e,a=e.context||this.context,f=C(a);if(this.debug(`checkFlag: ${s}`,{context:a,fallback:r}),this.isOffline())return this.debug(`checkFlag offline result: ${s}`,{value:r,offlineMode:!0}),r;if(!this.useWebSocket){let o=`${this.apiUrl}/flags/${s}/check`;return fetch(o,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(a)}).then(u=>{if(!u.ok)throw new Error("Network response was not ok");return u.json()}).then(u=>{let d=T(u);this.debug(`checkFlag result: ${s}`,d);let y=O(d.data);return typeof y.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(y),this.submitFlagCheckEvent(s,y,a),y.value}).catch(u=>{console.error("There was a problem with the fetch operation:",u);let d={flag:s,value:r,reason:"API request failed",error:u instanceof Error?u.message:String(u)};return this.submitFlagCheckEvent(s,d,a),r})}try{let o=this.checks[f];if(this.conn!==null&&typeof o<"u"&&typeof o[s]<"u")return this.debug(`checkFlag cached result: ${s}`,o[s]),o[s].value;if(this.isOffline())return r;try{await this.setContext(a)}catch(F){return console.error("WebSocket connection failed, falling back to REST:",F),this.fallbackToRest(s,a,r)}let d=(this.checks[f]??{})[s],y=d?.value??r;return this.debug(`checkFlag WebSocket result: ${s}`,typeof d<"u"?d:{value:r,fallbackUsed:!0}),typeof d<"u"&&this.submitFlagCheckEvent(s,d,a),y}catch(o){console.error("Unexpected error in checkFlag:",o);let u={flag:s,value:r,reason:"Unexpected error in flag check",error:o instanceof Error?o.message:String(o)};return this.submitFlagCheckEvent(s,u,a),r}}debug(e,...r){this.debugEnabled&&console.log(`[Schematic] ${e}`,...r)}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(e,r,s){let a={flagKey:e,value:r.value,reason:r.reason,flagId:r.flagId,ruleId:r.ruleId,companyId:r.companyId,userId:r.userId,error:r.error,reqCompany:s.company,reqUser:s.user};return this.debug("submitting flag check event:",a),this.handleEvent("flag_check",N(a))}async fallbackToRest(e,r,s){if(this.isOffline())return this.debug(`fallbackToRest offline result: ${e}`,{value:s,offlineMode:!0}),s;try{let a=`${this.apiUrl}/flags/${e}/check`,f=await fetch(a,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(r)});if(!f.ok)throw new Error("Network response was not ok");let o=await f.json(),u=T(o);this.debug(`fallbackToRest result: ${e}`,u);let d=O(u.data);return typeof d.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(d),this.submitFlagCheckEvent(e,d,r),d.value}catch(a){console.error("REST API call failed, using fallback value:",a);let f={flag:e,value:s,reason:"API request failed (fallback)",error:a instanceof Error?a.message:String(a)};return this.submitFlagCheckEvent(e,f,r),s}}checkFlags=async e=>{if(e=e||this.context,this.debug("checkFlags",{context:e}),this.isOffline())return this.debug("checkFlags offline result: returning empty object"),{};let r=`${this.apiUrl}/flags/check`,s=JSON.stringify(e);return fetch(r,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:s}).then(a=>{if(!a.ok)throw new Error("Network response was not ok");return a.json()}).then(a=>{let f=J(a);return this.debug("checkFlags result:",f),(f?.data?.flags??[]).reduce((o,u)=>(o[u.flag]=u.value,o),{})}).catch(a=>(console.error("There was a problem with the fetch operation:",a),{}))};identify=e=>{this.debug("identify:",e);try{this.setContext({company:e.company?.keys,user:e.keys})}catch(r){console.error("Error setting context:",r)}return this.handleEvent("identify",e)};setContext=async e=>{if(this.isOffline()||!this.useWebSocket)return this.context=e,this.setIsPending(!1),Promise.resolve();try{this.setIsPending(!0),this.conn||(this.conn=this.wsConnect());let r=await this.conn;await this.wsSendMessage(r,e)}catch(r){throw console.error("Failed to establish WebSocket connection:",r),r}};track=e=>{let{company:r,user:s,event:a,traits:f,quantity:o=1}=e,u={company:r??this.context.company,event:a,traits:f??{},user:s??this.context.user,quantity:o};return this.debug("track:",u),a in this.featureUsageEventMap&&this.optimisticallyUpdateFeatureUsage(a,o),this.handleEvent("track",u)};optimisticallyUpdateFeatureUsage=(e,r=1)=>{let s=this.featureUsageEventMap[e];s!=null&&(this.debug(`Optimistically updating feature usage for event: ${e}`,{quantity:r}),Object.entries(s).forEach(([a,f])=>{if(f===void 0)return;let o={...f};if(typeof o.featureUsage=="number"){if(o.featureUsage+=r,typeof o.featureAllocation=="number"){let d=o.featureUsageExceeded===!0,y=o.featureUsage>=o.featureAllocation;y!==d&&(o.featureUsageExceeded=y,y&&(o.value=!1),this.debug(`Usage limit status changed for flag: ${a}`,{was:d?"exceeded":"within limits",now:y?"exceeded":"within limits",featureUsage:o.featureUsage,featureAllocation:o.featureAllocation,value:o.value}))}this.featureUsageEventMap[e]!==void 0&&(this.featureUsageEventMap[e][a]=o);let u=C(this.context);this.checks[u]!==void 0&&this.checks[u]!==null&&(this.checks[u][a]=o),this.notifyFlagCheckListeners(a,o),this.notifyFlagValueListeners(a,o.value)}}))};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let e=this.eventQueue.shift();e&&this.sendEvent(e)}};getAnonymousId=()=>{if(!this.storage)return _();let e=this.storage.getItem(Q);if(typeof e<"u")return e;let r=_();return this.storage.setItem(Q,r),r};handleEvent=(e,r)=>{let s={api_key:this.apiKey,body:r,sent_at:new Date().toISOString(),tracker_event_id:_(),tracker_user_id:this.getAnonymousId(),type:e};return document?.hidden?this.storeEvent(s):this.sendEvent(s)};sendEvent=async e=>{let r=`${this.eventUrl}/e`,s=JSON.stringify(e);if(this.debug("sending event:",{url:r,event:e}),this.isOffline())return this.debug("event not sent (offline mode):",{event:e}),Promise.resolve();try{let a=await fetch(r,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:s});this.debug("event sent:",{status:a.status,statusText:a.statusText})}catch(a){console.error("Error sending Schematic event: ",a)}return Promise.resolve()};storeEvent=e=>(this.eventQueue.push(e),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(e){console.error("Error during cleanup:",e)}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((e,r)=>{let s=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",s);let a=new WebSocket(s);a.onopen=()=>{this.debug("WebSocket connection opened"),e(a)},a.onerror=f=>{this.debug("WebSocket connection error:",f),r(f)},a.onclose=()=>{this.debug("WebSocket connection closed"),this.conn=null}});wsSendMessage=(e,r)=>this.isOffline()?(this.debug("wsSendMessage: skipped (offline mode)"),this.setIsPending(!1),Promise.resolve()):new Promise((s,a)=>{if(C(r)==C(this.context))return this.debug("WebSocket context unchanged, skipping update"),s(this.setIsPending(!1));this.debug("WebSocket context updated:",r),this.context=r;let f=()=>{let o=!1,u=F=>{let g=JSON.parse(F.data);this.debug("WebSocket message received:",g),C(r)in this.checks||(this.checks[C(r)]={}),(g.flags??[]).forEach(v=>{let m=O(v),S=C(r);this.checks[S]===void 0&&(this.checks[S]={}),this.checks[S][m.flag]=m,this.debug("WebSocket flag update:",{flag:m.flag,value:m.value,flagCheck:m}),typeof m.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(m),(this.flagCheckListeners[v.flag]?.size>0||this.flagValueListeners[v.flag]?.size>0)&&this.submitFlagCheckEvent(m.flag,m,r),this.notifyFlagCheckListeners(v.flag,m),this.notifyFlagValueListeners(v.flag,m.value)}),this.setIsPending(!1),o||(o=!0,s())};e.addEventListener("message",u);let d=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${M}`,y={apiKey:this.apiKey,clientVersion:d,data:r};this.debug("WebSocket sending message:",y),e.send(JSON.stringify(y))};e.readyState===WebSocket.OPEN?(this.debug("WebSocket already open, sending message"),f()):e.readyState===WebSocket.CONNECTING?(this.debug("WebSocket connecting, waiting for open to send message"),e.addEventListener("open",f)):(this.debug("WebSocket is closed, cannot send message"),a("WebSocket is not open or connecting"))});getIsPending=()=>this.isPending;addIsPendingListener=e=>(this.isPendingListeners.add(e),()=>{this.isPendingListeners.delete(e)});setIsPending=e=>{this.isPending=e,this.isPendingListeners.forEach(r=>Ee(r,e))};getFlagCheck=e=>{let r=C(this.context);return(this.checks[r]??{})[e]};getFlagValue=e=>this.getFlagCheck(e)?.value;addFlagValueListener=(e,r)=>(e in this.flagValueListeners||(this.flagValueListeners[e]=new Set),this.flagValueListeners[e].add(r),()=>{this.flagValueListeners[e].delete(r)});addFlagCheckListener=(e,r)=>(e in this.flagCheckListeners||(this.flagCheckListeners[e]=new Set),this.flagCheckListeners[e].add(r),()=>{this.flagCheckListeners[e].delete(r)});notifyFlagCheckListeners=(e,r)=>{let s=this.flagCheckListeners?.[e]??[];s.size>0&&this.debug(`Notifying ${s.size} flag check listeners for ${e}`,r),typeof r.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(r),s.forEach(a=>Fe(a,r))};updateFeatureUsageEventMap=e=>{if(typeof e.featureUsageEvent!="string")return;let r=e.featureUsageEvent;(this.featureUsageEventMap[r]===void 0||this.featureUsageEventMap[r]===null)&&(this.featureUsageEventMap[r]={}),this.featureUsageEventMap[r]!==void 0&&(this.featureUsageEventMap[r][e.flag]=e),this.debug(`Updated featureUsageEventMap for event: ${r}, flag: ${e.flag}`,e)};notifyFlagValueListeners=(e,r)=>{let s=this.flagValueListeners?.[e]??[];s.size>0&&this.debug(`Notifying ${s.size} flag value listeners for ${e}`,{value:r}),s.forEach(a=>Ce(a,r))}},Ee=(n,e)=>{n.length>0?n(e):n()},Fe=(n,e)=>{n.length>0?n(e):n()},Ce=(n,e)=>{n.length>0?n(e):n()};window.Schematic=D;})();
|
|
1
|
+
"use strict";(()=>{var re=Object.create;var W=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var le=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports);var ue=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of ae(e))!oe.call(r,a)&&a!==t&&W(r,a,{get:()=>e[a],enumerable:!(s=se(e,a))||s.enumerable});return r};var ce=(r,e,t)=>(t=r!=null?re(ie(r)):{},ue(e||!r||!r.__esModule?W(t,"default",{value:r,enumerable:!0}):t,r));var z=le(Q=>{(function(r){var e=(function(t){var s=typeof globalThis<"u"&&globalThis||typeof r<"u"&&r||typeof global<"u"&&global||{},a={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(n){return n&&DataView.prototype.isPrototypeOf(n)}if(a.arrayBuffer)var o=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],u=ArrayBuffer.isView||function(n){return n&&o.indexOf(Object.prototype.toString.call(n))>-1};function f(n){if(typeof n!="string"&&(n=String(n)),/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(n)||n==="")throw new TypeError('Invalid character in header field name: "'+n+'"');return n.toLowerCase()}function y(n){return typeof n!="string"&&(n=String(n)),n}function F(n){var i={next:function(){var l=n.shift();return{done:l===void 0,value:l}}};return a.iterable&&(i[Symbol.iterator]=function(){return i}),i}function g(n){this.map={},n instanceof g?n.forEach(function(i,l){this.append(l,i)},this):Array.isArray(n)?n.forEach(function(i){if(i.length!=2)throw new TypeError("Headers constructor: expected name/value pair to be length 2, found"+i.length);this.append(i[0],i[1])},this):n&&Object.getOwnPropertyNames(n).forEach(function(i){this.append(i,n[i])},this)}g.prototype.append=function(n,i){n=f(n),i=y(i);var l=this.map[n];this.map[n]=l?l+", "+i:i},g.prototype.delete=function(n){delete this.map[f(n)]},g.prototype.get=function(n){return n=f(n),this.has(n)?this.map[n]:null},g.prototype.has=function(n){return this.map.hasOwnProperty(f(n))},g.prototype.set=function(n,i){this.map[f(n)]=y(i)},g.prototype.forEach=function(n,i){for(var l in this.map)this.map.hasOwnProperty(l)&&n.call(i,this.map[l],l,this)},g.prototype.keys=function(){var n=[];return this.forEach(function(i,l){n.push(l)}),F(n)},g.prototype.values=function(){var n=[];return this.forEach(function(i){n.push(i)}),F(n)},g.prototype.entries=function(){var n=[];return this.forEach(function(i,l){n.push([l,i])}),F(n)},a.iterable&&(g.prototype[Symbol.iterator]=g.prototype.entries);function k(n){if(!n._noBody){if(n.bodyUsed)return Promise.reject(new TypeError("Already read"));n.bodyUsed=!0}}function m(n){return new Promise(function(i,l){n.onload=function(){i(n.result)},n.onerror=function(){l(n.error)}})}function x(n){var i=new FileReader,l=m(i);return i.readAsArrayBuffer(n),l}function V(n){var i=new FileReader,l=m(i),h=/charset=([A-Za-z0-9_-]+)/.exec(n.type),p=h?h[1]:"utf-8";return i.readAsText(n,p),l}function Y(n){for(var i=new Uint8Array(n),l=new Array(i.length),h=0;h<i.length;h++)l[h]=String.fromCharCode(i[h]);return l.join("")}function q(n){if(n.slice)return n.slice(0);var i=new Uint8Array(n.byteLength);return i.set(new Uint8Array(n)),i.buffer}function $(){return this.bodyUsed=!1,this._initBody=function(n){this.bodyUsed=this.bodyUsed,this._bodyInit=n,n?typeof n=="string"?this._bodyText=n:a.blob&&Blob.prototype.isPrototypeOf(n)?this._bodyBlob=n:a.formData&&FormData.prototype.isPrototypeOf(n)?this._bodyFormData=n:a.searchParams&&URLSearchParams.prototype.isPrototypeOf(n)?this._bodyText=n.toString():a.arrayBuffer&&a.blob&&c(n)?(this._bodyArrayBuffer=q(n.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):a.arrayBuffer&&(ArrayBuffer.prototype.isPrototypeOf(n)||u(n))?this._bodyArrayBuffer=q(n):this._bodyText=n=Object.prototype.toString.call(n):(this._noBody=!0,this._bodyText=""),this.headers.get("content-type")||(typeof n=="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(n)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},a.blob&&(this.blob=function(){var n=k(this);if(n)return n;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 n=k(this);return n||(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(x);throw new Error("could not read as ArrayBuffer")}},this.text=function(){var n=k(this);if(n)return n;if(this._bodyBlob)return V(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(Y(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},a.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(n){var i=n.toUpperCase();return Z.indexOf(i)>-1?i:n}function S(n,i){if(!(this instanceof S))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');i=i||{};var l=i.body;if(n instanceof S){if(n.bodyUsed)throw new TypeError("Already read");this.url=n.url,this.credentials=n.credentials,i.headers||(this.headers=new g(n.headers)),this.method=n.method,this.mode=n.mode,this.signal=n.signal,!l&&n._bodyInit!=null&&(l=n._bodyInit,n.bodyUsed=!0)}else this.url=String(n);if(this.credentials=i.credentials||this.credentials||"same-origin",(i.headers||!this.headers)&&(this.headers=new g(i.headers)),this.method=j(i.method||this.method||"GET"),this.mode=i.mode||this.mode||null,this.signal=i.signal||this.signal||(function(){if("AbortController"in s){var d=new AbortController;return d.signal}})(),this.referrer=null,(this.method==="GET"||this.method==="HEAD")&&l)throw new TypeError("Body not allowed for GET or HEAD requests");if(this._initBody(l),(this.method==="GET"||this.method==="HEAD")&&(i.cache==="no-store"||i.cache==="no-cache")){var h=/([?&])_=[^&]*/;if(h.test(this.url))this.url=this.url.replace(h,"$1_="+new Date().getTime());else{var p=/\?/;this.url+=(p.test(this.url)?"&":"?")+"_="+new Date().getTime()}}}S.prototype.clone=function(){return new S(this,{body:this._bodyInit})};function ee(n){var i=new FormData;return n.trim().split("&").forEach(function(l){if(l){var h=l.split("="),p=h.shift().replace(/\+/g," "),d=h.join("=").replace(/\+/g," ");i.append(decodeURIComponent(p),decodeURIComponent(d))}}),i}function te(n){var i=new g,l=n.replace(/\r?\n[\t ]+/g," ");return l.split("\r").map(function(h){return h.indexOf(`
|
|
2
|
+
`)===0?h.substr(1,h.length):h}).forEach(function(h){var p=h.split(":"),d=p.shift().trim();if(d){var U=p.join(":").trim();try{i.append(d,U)}catch(A){console.warn("Response "+A.message)}}}),i}$.call(S.prototype);function E(n,i){if(!(this instanceof E))throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');if(i||(i={}),this.type="default",this.status=i.status===void 0?200:i.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=i.statusText===void 0?"":""+i.statusText,this.headers=new g(i.headers),this.url=i.url||"",this._initBody(n)}$.call(E.prototype),E.prototype.clone=function(){return new E(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new g(this.headers),url:this.url})},E.error=function(){var n=new E(null,{status:200,statusText:""});return n.ok=!1,n.status=0,n.type="error",n};var ne=[301,302,303,307,308];E.redirect=function(n,i){if(ne.indexOf(i)===-1)throw new RangeError("Invalid status code");return new E(null,{status:i,headers:{location:n}})},t.DOMException=s.DOMException;try{new t.DOMException}catch{t.DOMException=function(i,l){this.message=i,this.name=l;var h=Error(i);this.stack=h.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function I(n,i){return new Promise(function(l,h){var p=new S(n,i);if(p.signal&&p.signal.aborted)return h(new t.DOMException("Aborted","AbortError"));var d=new XMLHttpRequest;function U(){d.abort()}d.onload=function(){var b={statusText:d.statusText,headers:te(d.getAllResponseHeaders()||"")};p.url.indexOf("file://")===0&&(d.status<200||d.status>599)?b.status=200:b.status=d.status,b.url="responseURL"in d?d.responseURL:b.headers.get("X-Request-URL");var R="response"in d?d.response:d.responseText;setTimeout(function(){l(new E(R,b))},0)},d.onerror=function(){setTimeout(function(){h(new TypeError("Network request failed"))},0)},d.ontimeout=function(){setTimeout(function(){h(new TypeError("Network request timed out"))},0)},d.onabort=function(){setTimeout(function(){h(new t.DOMException("Aborted","AbortError"))},0)};function A(b){try{return b===""&&s.location.href?s.location.href:b}catch{return b}}if(d.open(p.method,A(p.url),!0),p.credentials==="include"?d.withCredentials=!0:p.credentials==="omit"&&(d.withCredentials=!1),"responseType"in d&&(a.blob?d.responseType="blob":a.arrayBuffer&&(d.responseType="arraybuffer")),i&&typeof i.headers=="object"&&!(i.headers instanceof g||s.Headers&&i.headers instanceof s.Headers)){var H=[];Object.getOwnPropertyNames(i.headers).forEach(function(b){H.push(f(b)),d.setRequestHeader(b,y(i.headers[b]))}),p.headers.forEach(function(b,R){H.indexOf(R)===-1&&d.setRequestHeader(R,b)})}else p.headers.forEach(function(b,R){d.setRequestHeader(R,b)});p.signal&&(p.signal.addEventListener("abort",U),d.onreadystatechange=function(){d.readyState===4&&p.signal.removeEventListener("abort",U)}),d.send(typeof p._bodyInit>"u"?null:p._bodyInit)})}return I.polyfill=!0,s.fetch||(s.fetch=I,s.Headers=g,s.Request=S,s.Response=E),t.Headers=g,t.Request=S,t.Response=E,t.fetch=I,t})({})})(typeof self<"u"?self:Q)});var v=[];for(let r=0;r<256;++r)v.push((r+256).toString(16).slice(1));function K(r,e=0){return(v[r[e+0]]+v[r[e+1]]+v[r[e+2]]+v[r[e+3]]+"-"+v[r[e+4]]+v[r[e+5]]+"-"+v[r[e+6]]+v[r[e+7]]+"-"+v[r[e+8]]+v[r[e+9]]+"-"+v[r[e+10]]+v[r[e+11]]+v[r[e+12]]+v[r[e+13]]+v[r[e+14]]+v[r[e+15]]).toLowerCase()}var P,de=new Uint8Array(16);function B(){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,e,t){r=r||{};let s=r.random??r.rng?.()??B();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,e){if(t=t||0,t<0||t+16>e.length)throw new RangeError(`UUID byte range ${t}:${t+15} is out of buffer bounds`);for(let a=0;a<16;++a)e[t+a]=s[a];return e}return K(s)}function ge(r,e,t){return L.randomUUID&&!e&&!r?L.randomUUID():he(r,e,t)}var _=ge;var st=ce(z());function w(r){return pe(r,!1)}function pe(r,e){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,featureUsageEvent:r.feature_usage_event==null?void 0:r.feature_usage_event,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 N(r){return ye(r,!1)}function ye(r,e=!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,e){return r==null?r:{data:w(r.data),params:r.params}}function X(r){return ve(r,!1)}function ve(r,e){return r==null?r:{flags:r.flags.map(w)}}function J(r){return ke(r,!1)}function ke(r,e){return r==null?r:{data:X(r.data),params:r.params}}var O=r=>{let{companyId:e,error:t,featureAllocation:s,featureUsage:a,featureUsageEvent:c,featureUsagePeriod:o,featureUsageResetAt:u,flag:f,flagId:y,reason:F,ruleId:g,ruleType:k,userId:m,value:x}=w(r);return{featureUsageExceeded:!x&&(k=="company_override_usage_exceeded"||k=="plan_entitlement_usage_exceeded"),companyId:e??void 0,error:t??void 0,featureAllocation:s??void 0,featureUsage:a??void 0,featureUsageEvent:c===null?void 0:c,featureUsagePeriod:o??void 0,featureUsageResetAt:u??void 0,flag:f,flagId:y??void 0,reason:F,ruleId:g??void 0,ruleType:k??void 0,userId:m??void 0,value:x}};function C(r){let e=Object.keys(r).reduce((t,s)=>{let c=Object.keys(r[s]||{}).sort().reduce((o,u)=>(o[u]=r[s][u],o),{});return t[s]=c,t},{});return JSON.stringify(e)}var M="1.2.5";var G="schematicId";var D=class{additionalHeaders={};apiKey;apiUrl="https://api.schematichq.com";conn=null;context={};debugEnabled=!1;offlineEnabled=!1;eventQueue;contextDependentEventQueue;eventUrl="https://c.schematichq.com";flagCheckListeners={};flagValueListeners={};isPending=!0;isPendingListeners=new Set;storage;useWebSocket=!1;checks={};featureUsageEventMap={};webSocketUrl="wss://api.schematichq.com";constructor(e,t){if(this.apiKey=e,this.eventQueue=[],this.contextDependentEventQueue=[],this.useWebSocket=t?.useWebSocket??!1,this.debugEnabled=t?.debug??!1,this.offlineEnabled=t?.offline??!1,typeof window<"u"&&typeof window.location<"u"){let s=new URLSearchParams(window.location.search),a=s.get("schematic_debug");a!==null&&(a===""||a==="true"||a==="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&&t?.debug!==!1&&(this.debugEnabled=!0),this.offlineEnabled&&this.setIsPending(!1),this.additionalHeaders={"X-Schematic-Client-Version":`schematic-js@${M}`,...t?.additionalHeaders??{}},t?.storage?this.storage=t.storage:typeof localStorage<"u"&&(this.storage=localStorage),t?.apiUrl!==void 0&&(this.apiUrl=t.apiUrl),t?.eventUrl!==void 0&&(this.eventUrl=t.eventUrl),t?.webSocketUrl!==void 0&&(this.webSocketUrl=t.webSocketUrl),typeof window<"u"&&window?.addEventListener&&window.addEventListener("beforeunload",()=>{this.flushEventQueue(),this.flushContextDependentEventQueue()}),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(e){let{fallback:t=!1,key:s}=e,a=e.context||this.context,c=C(a);if(this.debug(`checkFlag: ${s}`,{context:a,fallback:t}),this.isOffline())return this.debug(`checkFlag offline result: ${s}`,{value:t,offlineMode:!0}),t;if(!this.useWebSocket){let o=`${this.apiUrl}/flags/${s}/check`;return fetch(o,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(a)}).then(u=>{if(!u.ok)throw new Error("Network response was not ok");return u.json()}).then(u=>{let f=T(u);this.debug(`checkFlag result: ${s}`,f);let y=O(f.data);return typeof y.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(y),this.submitFlagCheckEvent(s,y,a),y.value}).catch(u=>{console.error("There was a problem with the fetch operation:",u);let f={flag:s,value:t,reason:"API request failed",error:u instanceof Error?u.message:String(u)};return this.submitFlagCheckEvent(s,f,a),t})}try{let o=this.checks[c];if(this.conn!==null&&typeof o<"u"&&typeof o[s]<"u")return this.debug(`checkFlag cached result: ${s}`,o[s]),o[s].value;if(this.isOffline())return t;try{await this.setContext(a)}catch(F){return console.error("WebSocket connection failed, falling back to REST:",F),this.fallbackToRest(s,a,t)}let f=(this.checks[c]??{})[s],y=f?.value??t;return this.debug(`checkFlag WebSocket result: ${s}`,typeof f<"u"?f:{value:t,fallbackUsed:!0}),typeof f<"u"&&this.submitFlagCheckEvent(s,f,a),y}catch(o){console.error("Unexpected error in checkFlag:",o);let u={flag:s,value:t,reason:"Unexpected error in flag check",error:o instanceof Error?o.message:String(o)};return this.submitFlagCheckEvent(s,u,a),t}}debug(e,...t){this.debugEnabled&&console.log(`[Schematic] ${e}`,...t)}isOffline(){return this.offlineEnabled}submitFlagCheckEvent(e,t,s){let a={flagKey:e,value:t.value,reason:t.reason,flagId:t.flagId,ruleId:t.ruleId,companyId:t.companyId,userId:t.userId,error:t.error,reqCompany:s.company,reqUser:s.user};return this.debug("submitting flag check event:",a),this.handleEvent("flag_check",N(a))}async fallbackToRest(e,t,s){if(this.isOffline())return this.debug(`fallbackToRest offline result: ${e}`,{value:s,offlineMode:!0}),s;try{let a=`${this.apiUrl}/flags/${e}/check`,c=await fetch(a,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:JSON.stringify(t)});if(!c.ok)throw new Error("Network response was not ok");let o=await c.json(),u=T(o);this.debug(`fallbackToRest result: ${e}`,u);let f=O(u.data);return typeof f.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(f),this.submitFlagCheckEvent(e,f,t),f.value}catch(a){console.error("REST API call failed, using fallback value:",a);let c={flag:e,value:s,reason:"API request failed (fallback)",error:a instanceof Error?a.message:String(a)};return this.submitFlagCheckEvent(e,c,t),s}}checkFlags=async e=>{if(e=e||this.context,this.debug("checkFlags",{context:e}),this.isOffline())return this.debug("checkFlags offline result: returning empty object"),{};let t=`${this.apiUrl}/flags/check`,s=JSON.stringify(e);return fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:s}).then(a=>{if(!a.ok)throw new Error("Network response was not ok");return a.json()}).then(a=>{let c=J(a);return this.debug("checkFlags result:",c),(c?.data?.flags??[]).reduce((o,u)=>(o[u.flag]=u.value,o),{})}).catch(a=>(console.error("There was a problem with the fetch operation:",a),{}))};identify=e=>{this.debug("identify:",e);try{this.setContext({company:e.company?.keys,user:e.keys})}catch(t){console.error("Error setting context:",t)}return this.handleEvent("identify",e)};setContext=async e=>{if(this.isOffline()||!this.useWebSocket)return this.context=e,this.flushContextDependentEventQueue(),this.setIsPending(!1),Promise.resolve();try{this.setIsPending(!0),this.conn||(this.conn=this.wsConnect());let t=await this.conn;await this.wsSendMessage(t,e)}catch(t){throw console.error("Failed to establish WebSocket connection:",t),t}};track=e=>{let{company:t,user:s,event:a,traits:c,quantity:o=1}=e;if(!this.hasContext(t,s)){this.debug(`track: queuing event "${a}" until context is available`);let f={api_key:this.apiKey,body:{company:t,event:a,traits:c??{},user:s,quantity:o},sent_at:new Date().toISOString(),tracker_event_id:_(),tracker_user_id:this.getAnonymousId(),type:"track"};return this.contextDependentEventQueue.push(f),Promise.resolve()}let u={company:t??this.context.company,event:a,traits:c??{},user:s??this.context.user,quantity:o};return this.debug("track:",u),a in this.featureUsageEventMap&&this.optimisticallyUpdateFeatureUsage(a,o),this.handleEvent("track",u)};optimisticallyUpdateFeatureUsage=(e,t=1)=>{let s=this.featureUsageEventMap[e];s!=null&&(this.debug(`Optimistically updating feature usage for event: ${e}`,{quantity:t}),Object.entries(s).forEach(([a,c])=>{if(c===void 0)return;let o={...c};if(typeof o.featureUsage=="number"){if(o.featureUsage+=t,typeof o.featureAllocation=="number"){let f=o.featureUsageExceeded===!0,y=o.featureUsage>=o.featureAllocation;y!==f&&(o.featureUsageExceeded=y,y&&(o.value=!1),this.debug(`Usage limit status changed for flag: ${a}`,{was:f?"exceeded":"within limits",now:y?"exceeded":"within limits",featureUsage:o.featureUsage,featureAllocation:o.featureAllocation,value:o.value}))}this.featureUsageEventMap[e]!==void 0&&(this.featureUsageEventMap[e][a]=o);let u=C(this.context);this.checks[u]!==void 0&&this.checks[u]!==null&&(this.checks[u][a]=o),this.notifyFlagCheckListeners(a,o),this.notifyFlagValueListeners(a,o.value)}}))};hasContext=(e,t)=>{let s=e!=null&&Object.keys(e).length>0||t!=null&&Object.keys(t).length>0,a=this.context.company!==void 0&&this.context.company!==null&&Object.keys(this.context.company).length>0||this.context.user!==void 0&&this.context.user!==null&&Object.keys(this.context.user).length>0;return s||a};flushContextDependentEventQueue=()=>{for(this.debug(`flushing ${this.contextDependentEventQueue.length} context-dependent events`);this.contextDependentEventQueue.length>0;){let e=this.contextDependentEventQueue.shift();if(e)if(e.type==="track"&&typeof e.body=="object"&&e.body!==null){let t=e.body,s={...t,company:t.company??this.context.company,user:t.user??this.context.user},a={...e,body:s,sent_at:new Date().toISOString()};this.sendEvent(a)}else this.sendEvent(e)}};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let e=this.eventQueue.shift();e&&this.sendEvent(e)}};getAnonymousId=()=>{if(!this.storage)return _();let e=this.storage.getItem(G);if(typeof e<"u")return e;let t=_();return this.storage.setItem(G,t),t};handleEvent=(e,t)=>{let s={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:_(),tracker_user_id:this.getAnonymousId(),type:e};return document?.hidden?this.storeEvent(s):this.sendEvent(s)};sendEvent=async e=>{let t=`${this.eventUrl}/e`,s=JSON.stringify(e);if(this.debug("sending event:",{url:t,event:e}),this.isOffline())return this.debug("event not sent (offline mode):",{event:e}),Promise.resolve();try{let a=await fetch(t,{method:"POST",headers:{...this.additionalHeaders??{},"Content-Type":"application/json;charset=UTF-8"},body:s});this.debug("event sent:",{status:a.status,statusText:a.statusText})}catch(a){console.error("Error sending Schematic event: ",a)}return Promise.resolve()};storeEvent=e=>(this.eventQueue.push(e),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(e){console.error("Error during cleanup:",e)}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((e,t)=>{let s=`${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;this.debug("connecting to WebSocket:",s);let a=new WebSocket(s);a.onopen=()=>{this.debug("WebSocket connection opened"),e(a)},a.onerror=c=>{this.debug("WebSocket connection error:",c),t(c)},a.onclose=()=>{this.debug("WebSocket connection closed"),this.conn=null}});wsSendMessage=(e,t)=>this.isOffline()?(this.debug("wsSendMessage: skipped (offline mode)"),this.setIsPending(!1),Promise.resolve()):new Promise((s,a)=>{if(C(t)==C(this.context))return this.debug("WebSocket context unchanged, skipping update"),s(this.setIsPending(!1));this.debug("WebSocket context updated:",t),this.context=t;let c=()=>{let o=!1,u=F=>{let g=JSON.parse(F.data);this.debug("WebSocket message received:",g),C(t)in this.checks||(this.checks[C(t)]={}),(g.flags??[]).forEach(k=>{let m=O(k),x=C(t);this.checks[x]===void 0&&(this.checks[x]={}),this.checks[x][m.flag]=m,this.debug("WebSocket flag update:",{flag:m.flag,value:m.value,flagCheck:m}),typeof m.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(m),(this.flagCheckListeners[k.flag]?.size>0||this.flagValueListeners[k.flag]?.size>0)&&this.submitFlagCheckEvent(m.flag,m,t),this.notifyFlagCheckListeners(k.flag,m),this.notifyFlagValueListeners(k.flag,m.value)}),this.flushContextDependentEventQueue(),this.setIsPending(!1),o||(o=!0,s())};e.addEventListener("message",u);let f=this.additionalHeaders["X-Schematic-Client-Version"]??`schematic-js@${M}`,y={apiKey:this.apiKey,clientVersion:f,data:t};this.debug("WebSocket sending message:",y),e.send(JSON.stringify(y))};e.readyState===WebSocket.OPEN?(this.debug("WebSocket already open, sending message"),c()):e.readyState===WebSocket.CONNECTING?(this.debug("WebSocket connecting, waiting for open to send message"),e.addEventListener("open",c)):(this.debug("WebSocket is closed, cannot send message"),a("WebSocket is not open or connecting"))});getIsPending=()=>this.isPending;addIsPendingListener=e=>(this.isPendingListeners.add(e),()=>{this.isPendingListeners.delete(e)});setIsPending=e=>{this.isPending=e,this.isPendingListeners.forEach(t=>Ee(t,e))};getFlagCheck=e=>{let t=C(this.context);return(this.checks[t]??{})[e]};getFlagValue=e=>this.getFlagCheck(e)?.value;addFlagValueListener=(e,t)=>(e in this.flagValueListeners||(this.flagValueListeners[e]=new Set),this.flagValueListeners[e].add(t),()=>{this.flagValueListeners[e].delete(t)});addFlagCheckListener=(e,t)=>(e in this.flagCheckListeners||(this.flagCheckListeners[e]=new Set),this.flagCheckListeners[e].add(t),()=>{this.flagCheckListeners[e].delete(t)});notifyFlagCheckListeners=(e,t)=>{let s=this.flagCheckListeners?.[e]??[];s.size>0&&this.debug(`Notifying ${s.size} flag check listeners for ${e}`,t),typeof t.featureUsageEvent=="string"&&this.updateFeatureUsageEventMap(t),s.forEach(a=>Fe(a,t))};updateFeatureUsageEventMap=e=>{if(typeof e.featureUsageEvent!="string")return;let t=e.featureUsageEvent;(this.featureUsageEventMap[t]===void 0||this.featureUsageEventMap[t]===null)&&(this.featureUsageEventMap[t]={}),this.featureUsageEventMap[t]!==void 0&&(this.featureUsageEventMap[t][e.flag]=e),this.debug(`Updated featureUsageEventMap for event: ${t}, flag: ${e.flag}`,e)};notifyFlagValueListeners=(e,t)=>{let s=this.flagValueListeners?.[e]??[];s.size>0&&this.debug(`Notifying ${s.size} flag value listeners for ${e}`,{value:t}),s.forEach(a=>Ce(a,t))}},Ee=(r,e)=>{r.length>0?r(e):r()},Fe=(r,e)=>{r.length>0?r(e):r()},Ce=(r,e)=>{r.length>0?r(e):r()};window.Schematic=D;})();
|
|
3
3
|
/* @preserve */
|
package/dist/schematic.cjs.js
CHANGED
|
@@ -34,20 +34,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
34
34
|
var require_browser_polyfill = __commonJS({
|
|
35
35
|
"node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
|
|
36
36
|
(function(self2) {
|
|
37
|
-
var irrelevant = function(exports2) {
|
|
37
|
+
var irrelevant = (function(exports2) {
|
|
38
38
|
var g = typeof globalThis !== "undefined" && globalThis || typeof self2 !== "undefined" && self2 || // eslint-disable-next-line no-undef
|
|
39
39
|
typeof global !== "undefined" && global || {};
|
|
40
40
|
var support = {
|
|
41
41
|
searchParams: "URLSearchParams" in g,
|
|
42
42
|
iterable: "Symbol" in g && "iterator" in Symbol,
|
|
43
|
-
blob: "FileReader" in g && "Blob" in g && function() {
|
|
43
|
+
blob: "FileReader" in g && "Blob" in g && (function() {
|
|
44
44
|
try {
|
|
45
45
|
new Blob();
|
|
46
46
|
return true;
|
|
47
47
|
} catch (e) {
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
|
-
}(),
|
|
50
|
+
})(),
|
|
51
51
|
formData: "FormData" in g,
|
|
52
52
|
arrayBuffer: "ArrayBuffer" in g
|
|
53
53
|
};
|
|
@@ -349,12 +349,12 @@ var require_browser_polyfill = __commonJS({
|
|
|
349
349
|
}
|
|
350
350
|
this.method = normalizeMethod(options.method || this.method || "GET");
|
|
351
351
|
this.mode = options.mode || this.mode || null;
|
|
352
|
-
this.signal = options.signal || this.signal || function() {
|
|
352
|
+
this.signal = options.signal || this.signal || (function() {
|
|
353
353
|
if ("AbortController" in g) {
|
|
354
354
|
var ctrl = new AbortController();
|
|
355
355
|
return ctrl.signal;
|
|
356
356
|
}
|
|
357
|
-
}();
|
|
357
|
+
})();
|
|
358
358
|
this.referrer = null;
|
|
359
359
|
if ((this.method === "GET" || this.method === "HEAD") && body) {
|
|
360
360
|
throw new TypeError("Body not allowed for GET or HEAD requests");
|
|
@@ -561,7 +561,7 @@ var require_browser_polyfill = __commonJS({
|
|
|
561
561
|
exports2.Response = Response;
|
|
562
562
|
exports2.fetch = fetch2;
|
|
563
563
|
return exports2;
|
|
564
|
-
}({});
|
|
564
|
+
})({});
|
|
565
565
|
})(typeof self !== "undefined" ? self : exports);
|
|
566
566
|
}
|
|
567
567
|
});
|
|
@@ -579,7 +579,7 @@ __export(index_exports, {
|
|
|
579
579
|
});
|
|
580
580
|
module.exports = __toCommonJS(index_exports);
|
|
581
581
|
|
|
582
|
-
// node_modules/uuid/dist/
|
|
582
|
+
// node_modules/uuid/dist/stringify.js
|
|
583
583
|
var byteToHex = [];
|
|
584
584
|
for (let i = 0; i < 256; ++i) {
|
|
585
585
|
byteToHex.push((i + 256).toString(16).slice(1));
|
|
@@ -588,7 +588,7 @@ function unsafeStringify(arr, offset = 0) {
|
|
|
588
588
|
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
589
589
|
}
|
|
590
590
|
|
|
591
|
-
// node_modules/uuid/dist/
|
|
591
|
+
// node_modules/uuid/dist/rng.js
|
|
592
592
|
var getRandomValues;
|
|
593
593
|
var rnds8 = new Uint8Array(16);
|
|
594
594
|
function rng() {
|
|
@@ -601,15 +601,12 @@ function rng() {
|
|
|
601
601
|
return getRandomValues(rnds8);
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
-
// node_modules/uuid/dist/
|
|
604
|
+
// node_modules/uuid/dist/native.js
|
|
605
605
|
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
|
|
606
606
|
var native_default = { randomUUID };
|
|
607
607
|
|
|
608
|
-
// node_modules/uuid/dist/
|
|
609
|
-
function
|
|
610
|
-
if (native_default.randomUUID && !buf && !options) {
|
|
611
|
-
return native_default.randomUUID();
|
|
612
|
-
}
|
|
608
|
+
// node_modules/uuid/dist/v4.js
|
|
609
|
+
function _v4(options, buf, offset) {
|
|
613
610
|
options = options || {};
|
|
614
611
|
const rnds = options.random ?? options.rng?.() ?? rng();
|
|
615
612
|
if (rnds.length < 16) {
|
|
@@ -629,6 +626,12 @@ function v4(options, buf, offset) {
|
|
|
629
626
|
}
|
|
630
627
|
return unsafeStringify(rnds);
|
|
631
628
|
}
|
|
629
|
+
function v4(options, buf, offset) {
|
|
630
|
+
if (native_default.randomUUID && !buf && !options) {
|
|
631
|
+
return native_default.randomUUID();
|
|
632
|
+
}
|
|
633
|
+
return _v4(options, buf, offset);
|
|
634
|
+
}
|
|
632
635
|
var v4_default = v4;
|
|
633
636
|
|
|
634
637
|
// src/index.ts
|
|
@@ -797,7 +800,7 @@ function contextString(context) {
|
|
|
797
800
|
}
|
|
798
801
|
|
|
799
802
|
// src/version.ts
|
|
800
|
-
var version = "1.2.
|
|
803
|
+
var version = "1.2.5";
|
|
801
804
|
|
|
802
805
|
// src/index.ts
|
|
803
806
|
var anonymousIdKey = "schematicId";
|
|
@@ -810,6 +813,7 @@ var Schematic = class {
|
|
|
810
813
|
debugEnabled = false;
|
|
811
814
|
offlineEnabled = false;
|
|
812
815
|
eventQueue;
|
|
816
|
+
contextDependentEventQueue;
|
|
813
817
|
eventUrl = "https://c.schematichq.com";
|
|
814
818
|
flagCheckListeners = {};
|
|
815
819
|
flagValueListeners = {};
|
|
@@ -823,6 +827,7 @@ var Schematic = class {
|
|
|
823
827
|
constructor(apiKey, options) {
|
|
824
828
|
this.apiKey = apiKey;
|
|
825
829
|
this.eventQueue = [];
|
|
830
|
+
this.contextDependentEventQueue = [];
|
|
826
831
|
this.useWebSocket = options?.useWebSocket ?? false;
|
|
827
832
|
this.debugEnabled = options?.debug ?? false;
|
|
828
833
|
this.offlineEnabled = options?.offline ?? false;
|
|
@@ -865,6 +870,7 @@ var Schematic = class {
|
|
|
865
870
|
if (typeof window !== "undefined" && window?.addEventListener) {
|
|
866
871
|
window.addEventListener("beforeunload", () => {
|
|
867
872
|
this.flushEventQueue();
|
|
873
|
+
this.flushContextDependentEventQueue();
|
|
868
874
|
});
|
|
869
875
|
}
|
|
870
876
|
if (this.offlineEnabled) {
|
|
@@ -1123,6 +1129,7 @@ var Schematic = class {
|
|
|
1123
1129
|
setContext = async (context) => {
|
|
1124
1130
|
if (this.isOffline() || !this.useWebSocket) {
|
|
1125
1131
|
this.context = context;
|
|
1132
|
+
this.flushContextDependentEventQueue();
|
|
1126
1133
|
this.setIsPending(false);
|
|
1127
1134
|
return Promise.resolve();
|
|
1128
1135
|
}
|
|
@@ -1145,6 +1152,25 @@ var Schematic = class {
|
|
|
1145
1152
|
*/
|
|
1146
1153
|
track = (body) => {
|
|
1147
1154
|
const { company, user, event, traits, quantity = 1 } = body;
|
|
1155
|
+
if (!this.hasContext(company, user)) {
|
|
1156
|
+
this.debug(`track: queuing event "${event}" until context is available`);
|
|
1157
|
+
const queuedEvent = {
|
|
1158
|
+
api_key: this.apiKey,
|
|
1159
|
+
body: {
|
|
1160
|
+
company,
|
|
1161
|
+
event,
|
|
1162
|
+
traits: traits ?? {},
|
|
1163
|
+
user,
|
|
1164
|
+
quantity
|
|
1165
|
+
},
|
|
1166
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1167
|
+
tracker_event_id: v4_default(),
|
|
1168
|
+
tracker_user_id: this.getAnonymousId(),
|
|
1169
|
+
type: "track"
|
|
1170
|
+
};
|
|
1171
|
+
this.contextDependentEventQueue.push(queuedEvent);
|
|
1172
|
+
return Promise.resolve();
|
|
1173
|
+
}
|
|
1148
1174
|
const trackData = {
|
|
1149
1175
|
company: company ?? this.context.company,
|
|
1150
1176
|
event,
|
|
@@ -1207,6 +1233,38 @@ var Schematic = class {
|
|
|
1207
1233
|
/**
|
|
1208
1234
|
* Event processing
|
|
1209
1235
|
*/
|
|
1236
|
+
hasContext = (company, user) => {
|
|
1237
|
+
const hasProvidedContext = company !== void 0 && company !== null && Object.keys(company).length > 0 || user !== void 0 && user !== null && Object.keys(user).length > 0;
|
|
1238
|
+
const hasInstanceContext = this.context.company !== void 0 && this.context.company !== null && Object.keys(this.context.company).length > 0 || this.context.user !== void 0 && this.context.user !== null && Object.keys(this.context.user).length > 0;
|
|
1239
|
+
return hasProvidedContext || hasInstanceContext;
|
|
1240
|
+
};
|
|
1241
|
+
flushContextDependentEventQueue = () => {
|
|
1242
|
+
this.debug(
|
|
1243
|
+
`flushing ${this.contextDependentEventQueue.length} context-dependent events`
|
|
1244
|
+
);
|
|
1245
|
+
while (this.contextDependentEventQueue.length > 0) {
|
|
1246
|
+
const event = this.contextDependentEventQueue.shift();
|
|
1247
|
+
if (event) {
|
|
1248
|
+
if (event.type === "track" && typeof event.body === "object" && event.body !== null) {
|
|
1249
|
+
const trackBody = event.body;
|
|
1250
|
+
const updatedBody = {
|
|
1251
|
+
...trackBody,
|
|
1252
|
+
company: trackBody.company ?? this.context.company,
|
|
1253
|
+
user: trackBody.user ?? this.context.user
|
|
1254
|
+
};
|
|
1255
|
+
const updatedEvent = {
|
|
1256
|
+
...event,
|
|
1257
|
+
body: updatedBody,
|
|
1258
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1259
|
+
// Update timestamp to actual send time
|
|
1260
|
+
};
|
|
1261
|
+
this.sendEvent(updatedEvent);
|
|
1262
|
+
} else {
|
|
1263
|
+
this.sendEvent(event);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1210
1268
|
flushEventQueue = () => {
|
|
1211
1269
|
while (this.eventQueue.length > 0) {
|
|
1212
1270
|
const event = this.eventQueue.shift();
|
|
@@ -1365,6 +1423,7 @@ var Schematic = class {
|
|
|
1365
1423
|
this.notifyFlagCheckListeners(flag.flag, flagCheck);
|
|
1366
1424
|
this.notifyFlagValueListeners(flag.flag, flagCheck.value);
|
|
1367
1425
|
});
|
|
1426
|
+
this.flushContextDependentEventQueue();
|
|
1368
1427
|
this.setIsPending(false);
|
|
1369
1428
|
if (!resolved) {
|
|
1370
1429
|
resolved = true;
|
package/dist/schematic.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ declare interface CheckFlagResponse {
|
|
|
32
32
|
* Do not edit the class manually.
|
|
33
33
|
*/
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
35
|
+
*
|
|
36
36
|
* @export
|
|
37
37
|
* @interface CheckFlagResponseData
|
|
38
38
|
*/
|
|
@@ -183,7 +183,7 @@ declare interface CheckFlagsResponse {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/**
|
|
186
|
-
*
|
|
186
|
+
*
|
|
187
187
|
* @export
|
|
188
188
|
* @interface CheckFlagsResponseData
|
|
189
189
|
*/
|
|
@@ -357,6 +357,7 @@ export declare class Schematic {
|
|
|
357
357
|
private debugEnabled;
|
|
358
358
|
private offlineEnabled;
|
|
359
359
|
private eventQueue;
|
|
360
|
+
private contextDependentEventQueue;
|
|
360
361
|
private eventUrl;
|
|
361
362
|
private flagCheckListeners;
|
|
362
363
|
private flagValueListeners;
|
|
@@ -431,6 +432,8 @@ export declare class Schematic {
|
|
|
431
432
|
/**
|
|
432
433
|
* Event processing
|
|
433
434
|
*/
|
|
435
|
+
private hasContext;
|
|
436
|
+
private flushContextDependentEventQueue;
|
|
434
437
|
private flushEventQueue;
|
|
435
438
|
private getAnonymousId;
|
|
436
439
|
private handleEvent;
|
package/dist/schematic.esm.js
CHANGED
|
@@ -28,20 +28,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var require_browser_polyfill = __commonJS({
|
|
29
29
|
"node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
|
|
30
30
|
(function(self2) {
|
|
31
|
-
var irrelevant = function(exports2) {
|
|
31
|
+
var irrelevant = (function(exports2) {
|
|
32
32
|
var g = typeof globalThis !== "undefined" && globalThis || typeof self2 !== "undefined" && self2 || // eslint-disable-next-line no-undef
|
|
33
33
|
typeof global !== "undefined" && global || {};
|
|
34
34
|
var support = {
|
|
35
35
|
searchParams: "URLSearchParams" in g,
|
|
36
36
|
iterable: "Symbol" in g && "iterator" in Symbol,
|
|
37
|
-
blob: "FileReader" in g && "Blob" in g && function() {
|
|
37
|
+
blob: "FileReader" in g && "Blob" in g && (function() {
|
|
38
38
|
try {
|
|
39
39
|
new Blob();
|
|
40
40
|
return true;
|
|
41
41
|
} catch (e) {
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
44
|
-
}(),
|
|
44
|
+
})(),
|
|
45
45
|
formData: "FormData" in g,
|
|
46
46
|
arrayBuffer: "ArrayBuffer" in g
|
|
47
47
|
};
|
|
@@ -343,12 +343,12 @@ var require_browser_polyfill = __commonJS({
|
|
|
343
343
|
}
|
|
344
344
|
this.method = normalizeMethod(options.method || this.method || "GET");
|
|
345
345
|
this.mode = options.mode || this.mode || null;
|
|
346
|
-
this.signal = options.signal || this.signal || function() {
|
|
346
|
+
this.signal = options.signal || this.signal || (function() {
|
|
347
347
|
if ("AbortController" in g) {
|
|
348
348
|
var ctrl = new AbortController();
|
|
349
349
|
return ctrl.signal;
|
|
350
350
|
}
|
|
351
|
-
}();
|
|
351
|
+
})();
|
|
352
352
|
this.referrer = null;
|
|
353
353
|
if ((this.method === "GET" || this.method === "HEAD") && body) {
|
|
354
354
|
throw new TypeError("Body not allowed for GET or HEAD requests");
|
|
@@ -555,12 +555,12 @@ var require_browser_polyfill = __commonJS({
|
|
|
555
555
|
exports2.Response = Response;
|
|
556
556
|
exports2.fetch = fetch2;
|
|
557
557
|
return exports2;
|
|
558
|
-
}({});
|
|
558
|
+
})({});
|
|
559
559
|
})(typeof self !== "undefined" ? self : exports);
|
|
560
560
|
}
|
|
561
561
|
});
|
|
562
562
|
|
|
563
|
-
// node_modules/uuid/dist/
|
|
563
|
+
// node_modules/uuid/dist/stringify.js
|
|
564
564
|
var byteToHex = [];
|
|
565
565
|
for (let i = 0; i < 256; ++i) {
|
|
566
566
|
byteToHex.push((i + 256).toString(16).slice(1));
|
|
@@ -569,7 +569,7 @@ function unsafeStringify(arr, offset = 0) {
|
|
|
569
569
|
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
-
// node_modules/uuid/dist/
|
|
572
|
+
// node_modules/uuid/dist/rng.js
|
|
573
573
|
var getRandomValues;
|
|
574
574
|
var rnds8 = new Uint8Array(16);
|
|
575
575
|
function rng() {
|
|
@@ -582,15 +582,12 @@ function rng() {
|
|
|
582
582
|
return getRandomValues(rnds8);
|
|
583
583
|
}
|
|
584
584
|
|
|
585
|
-
// node_modules/uuid/dist/
|
|
585
|
+
// node_modules/uuid/dist/native.js
|
|
586
586
|
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
|
|
587
587
|
var native_default = { randomUUID };
|
|
588
588
|
|
|
589
|
-
// node_modules/uuid/dist/
|
|
590
|
-
function
|
|
591
|
-
if (native_default.randomUUID && !buf && !options) {
|
|
592
|
-
return native_default.randomUUID();
|
|
593
|
-
}
|
|
589
|
+
// node_modules/uuid/dist/v4.js
|
|
590
|
+
function _v4(options, buf, offset) {
|
|
594
591
|
options = options || {};
|
|
595
592
|
const rnds = options.random ?? options.rng?.() ?? rng();
|
|
596
593
|
if (rnds.length < 16) {
|
|
@@ -610,6 +607,12 @@ function v4(options, buf, offset) {
|
|
|
610
607
|
}
|
|
611
608
|
return unsafeStringify(rnds);
|
|
612
609
|
}
|
|
610
|
+
function v4(options, buf, offset) {
|
|
611
|
+
if (native_default.randomUUID && !buf && !options) {
|
|
612
|
+
return native_default.randomUUID();
|
|
613
|
+
}
|
|
614
|
+
return _v4(options, buf, offset);
|
|
615
|
+
}
|
|
613
616
|
var v4_default = v4;
|
|
614
617
|
|
|
615
618
|
// src/index.ts
|
|
@@ -778,7 +781,7 @@ function contextString(context) {
|
|
|
778
781
|
}
|
|
779
782
|
|
|
780
783
|
// src/version.ts
|
|
781
|
-
var version = "1.2.
|
|
784
|
+
var version = "1.2.5";
|
|
782
785
|
|
|
783
786
|
// src/index.ts
|
|
784
787
|
var anonymousIdKey = "schematicId";
|
|
@@ -791,6 +794,7 @@ var Schematic = class {
|
|
|
791
794
|
debugEnabled = false;
|
|
792
795
|
offlineEnabled = false;
|
|
793
796
|
eventQueue;
|
|
797
|
+
contextDependentEventQueue;
|
|
794
798
|
eventUrl = "https://c.schematichq.com";
|
|
795
799
|
flagCheckListeners = {};
|
|
796
800
|
flagValueListeners = {};
|
|
@@ -804,6 +808,7 @@ var Schematic = class {
|
|
|
804
808
|
constructor(apiKey, options) {
|
|
805
809
|
this.apiKey = apiKey;
|
|
806
810
|
this.eventQueue = [];
|
|
811
|
+
this.contextDependentEventQueue = [];
|
|
807
812
|
this.useWebSocket = options?.useWebSocket ?? false;
|
|
808
813
|
this.debugEnabled = options?.debug ?? false;
|
|
809
814
|
this.offlineEnabled = options?.offline ?? false;
|
|
@@ -846,6 +851,7 @@ var Schematic = class {
|
|
|
846
851
|
if (typeof window !== "undefined" && window?.addEventListener) {
|
|
847
852
|
window.addEventListener("beforeunload", () => {
|
|
848
853
|
this.flushEventQueue();
|
|
854
|
+
this.flushContextDependentEventQueue();
|
|
849
855
|
});
|
|
850
856
|
}
|
|
851
857
|
if (this.offlineEnabled) {
|
|
@@ -1104,6 +1110,7 @@ var Schematic = class {
|
|
|
1104
1110
|
setContext = async (context) => {
|
|
1105
1111
|
if (this.isOffline() || !this.useWebSocket) {
|
|
1106
1112
|
this.context = context;
|
|
1113
|
+
this.flushContextDependentEventQueue();
|
|
1107
1114
|
this.setIsPending(false);
|
|
1108
1115
|
return Promise.resolve();
|
|
1109
1116
|
}
|
|
@@ -1126,6 +1133,25 @@ var Schematic = class {
|
|
|
1126
1133
|
*/
|
|
1127
1134
|
track = (body) => {
|
|
1128
1135
|
const { company, user, event, traits, quantity = 1 } = body;
|
|
1136
|
+
if (!this.hasContext(company, user)) {
|
|
1137
|
+
this.debug(`track: queuing event "${event}" until context is available`);
|
|
1138
|
+
const queuedEvent = {
|
|
1139
|
+
api_key: this.apiKey,
|
|
1140
|
+
body: {
|
|
1141
|
+
company,
|
|
1142
|
+
event,
|
|
1143
|
+
traits: traits ?? {},
|
|
1144
|
+
user,
|
|
1145
|
+
quantity
|
|
1146
|
+
},
|
|
1147
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1148
|
+
tracker_event_id: v4_default(),
|
|
1149
|
+
tracker_user_id: this.getAnonymousId(),
|
|
1150
|
+
type: "track"
|
|
1151
|
+
};
|
|
1152
|
+
this.contextDependentEventQueue.push(queuedEvent);
|
|
1153
|
+
return Promise.resolve();
|
|
1154
|
+
}
|
|
1129
1155
|
const trackData = {
|
|
1130
1156
|
company: company ?? this.context.company,
|
|
1131
1157
|
event,
|
|
@@ -1188,6 +1214,38 @@ var Schematic = class {
|
|
|
1188
1214
|
/**
|
|
1189
1215
|
* Event processing
|
|
1190
1216
|
*/
|
|
1217
|
+
hasContext = (company, user) => {
|
|
1218
|
+
const hasProvidedContext = company !== void 0 && company !== null && Object.keys(company).length > 0 || user !== void 0 && user !== null && Object.keys(user).length > 0;
|
|
1219
|
+
const hasInstanceContext = this.context.company !== void 0 && this.context.company !== null && Object.keys(this.context.company).length > 0 || this.context.user !== void 0 && this.context.user !== null && Object.keys(this.context.user).length > 0;
|
|
1220
|
+
return hasProvidedContext || hasInstanceContext;
|
|
1221
|
+
};
|
|
1222
|
+
flushContextDependentEventQueue = () => {
|
|
1223
|
+
this.debug(
|
|
1224
|
+
`flushing ${this.contextDependentEventQueue.length} context-dependent events`
|
|
1225
|
+
);
|
|
1226
|
+
while (this.contextDependentEventQueue.length > 0) {
|
|
1227
|
+
const event = this.contextDependentEventQueue.shift();
|
|
1228
|
+
if (event) {
|
|
1229
|
+
if (event.type === "track" && typeof event.body === "object" && event.body !== null) {
|
|
1230
|
+
const trackBody = event.body;
|
|
1231
|
+
const updatedBody = {
|
|
1232
|
+
...trackBody,
|
|
1233
|
+
company: trackBody.company ?? this.context.company,
|
|
1234
|
+
user: trackBody.user ?? this.context.user
|
|
1235
|
+
};
|
|
1236
|
+
const updatedEvent = {
|
|
1237
|
+
...event,
|
|
1238
|
+
body: updatedBody,
|
|
1239
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1240
|
+
// Update timestamp to actual send time
|
|
1241
|
+
};
|
|
1242
|
+
this.sendEvent(updatedEvent);
|
|
1243
|
+
} else {
|
|
1244
|
+
this.sendEvent(event);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1191
1249
|
flushEventQueue = () => {
|
|
1192
1250
|
while (this.eventQueue.length > 0) {
|
|
1193
1251
|
const event = this.eventQueue.shift();
|
|
@@ -1346,6 +1404,7 @@ var Schematic = class {
|
|
|
1346
1404
|
this.notifyFlagCheckListeners(flag.flag, flagCheck);
|
|
1347
1405
|
this.notifyFlagValueListeners(flag.flag, flagCheck.value);
|
|
1348
1406
|
});
|
|
1407
|
+
this.flushContextDependentEventQueue();
|
|
1349
1408
|
this.setIsPending(false);
|
|
1350
1409
|
if (!resolved) {
|
|
1351
1410
|
resolved = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schematichq/schematic-js",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"main": "dist/schematic.cjs.js",
|
|
5
5
|
"module": "dist/schematic.esm.js",
|
|
6
6
|
"types": "dist/schematic.d.ts",
|
|
@@ -26,30 +26,33 @@
|
|
|
26
26
|
"format": "prettier --write src/*.ts",
|
|
27
27
|
"lint": "eslint src --report-unused-disable-directives --fix",
|
|
28
28
|
"openapi": "rm -rf src/types/api/ && npx openapi-generator-cli generate -c openapi-config.yaml --global-property models=\"EventBody:EventBodyFlagCheck:EventBodyIdentify:EventBodyIdentifyCompany:EventBodyTrack:CheckFlagResponse:CheckFlagResponseData:CheckFlagsResponse:CheckFlagsResponseData\",supportingFiles=runtime.ts && prettier --write \"src/types/api/**/*.{ts,tsx}\"",
|
|
29
|
-
"
|
|
29
|
+
"prepare": "husky",
|
|
30
|
+
"test": "jest --config jest.config.js",
|
|
31
|
+
"tsc": "npx tsc"
|
|
30
32
|
},
|
|
31
33
|
"dependencies": {
|
|
32
34
|
"cross-fetch": "^4.1.0",
|
|
33
|
-
"uuid": "^
|
|
35
|
+
"uuid": "^13.0.0"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
38
|
"@eslint/js": "^9.24.0",
|
|
37
39
|
"@microsoft/api-extractor": "^7.52.2",
|
|
38
40
|
"@openapitools/openapi-generator-cli": "^2.18.4",
|
|
39
|
-
"@types/jest": "^
|
|
41
|
+
"@types/jest": "^30.0.0",
|
|
40
42
|
"@types/uuid": "^10.0.0",
|
|
41
43
|
"esbuild": "^0.25.2",
|
|
42
44
|
"esbuild-jest": "^0.5.0",
|
|
43
45
|
"eslint": "^9.24.0",
|
|
44
46
|
"globals": "^16.0.0",
|
|
45
|
-
"
|
|
46
|
-
"jest
|
|
47
|
-
"jest-
|
|
47
|
+
"husky": "^9.1.7",
|
|
48
|
+
"jest": "^30.0.0",
|
|
49
|
+
"jest-environment-jsdom": "^30.0.0",
|
|
50
|
+
"jest-esbuild": "^0.4.0",
|
|
48
51
|
"jest-fetch-mock": "^3.0.3",
|
|
49
52
|
"mock-socket": "^9.3.1",
|
|
50
|
-
"prettier": "^3.
|
|
53
|
+
"prettier": "^3.6.2",
|
|
51
54
|
"ts-jest": "^29.3.0",
|
|
52
|
-
"typescript": "^5.
|
|
55
|
+
"typescript": "^5.9.2",
|
|
53
56
|
"typescript-eslint": "^8.29.1"
|
|
54
57
|
},
|
|
55
58
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|