@gradual-so/sdk 0.5.0 → 0.6.1

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/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';function d(i,t){let{contextKind:a,attributeKey:e,operator:o,value:r}=i,n=t[a]?.[e];switch(o){case "equals":return n===r;case "not_equals":return n!==r;case "contains":return typeof n=="string"&&typeof r=="string"||Array.isArray(n)?n.includes(r):false;case "not_contains":return typeof n=="string"&&typeof r=="string"||Array.isArray(n)?!n.includes(r):true;case "starts_with":return typeof n=="string"&&typeof r=="string"?n.startsWith(r):false;case "ends_with":return typeof n=="string"&&typeof r=="string"?n.endsWith(r):false;case "greater_than":return typeof n=="number"&&typeof r=="number"?n>r:false;case "less_than":return typeof n=="number"&&typeof r=="number"?n<r:false;case "greater_than_or_equal":return typeof n=="number"&&typeof r=="number"?n>=r:false;case "less_than_or_equal":return typeof n=="number"&&typeof r=="number"?n<=r:false;case "in":return Array.isArray(r)?r.includes(n):false;case "not_in":return Array.isArray(r)?!r.includes(n):true;case "exists":return n!=null;case "not_exists":return n==null;default:return false}}function u(i,t){return i.every(a=>d(a,t))}function p(i,t){return u(i.conditions,t)}function h(i,t,a){switch(i.type){case "individual":return i.contextKind&&i.attributeKey&&i.attributeValue!==void 0?t[i.contextKind]?.[i.attributeKey]===i.attributeValue:false;case "rule":return i.conditions?u(i.conditions,t):false;case "segment":if(i.segmentKey){let e=a[i.segmentKey];if(e)return p(e,t)}return false;default:return false}}function s(i,t,a){if(!i.enabled)return i.variations[i.offVariationKey]?.value;let e=[...i.targets].sort((r,n)=>r.sortOrder-n.sortOrder);for(let r of e)if(h(r,t,a)){let n=i.variations[r.variationKey];if(n)return n.value}return i.variations[i.defaultVariationKey]?.value}var c="https://worker.gradual.so/api/v1",l=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??c,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};let a=t.polling?.enabled??true,e=t.polling?.intervalMs??1e4;a&&this.initPromise.then(()=>{setInterval(async()=>{try{await this.refresh();}catch(o){console.warn("Gradual: Polling refresh failed",o);}},e);});}async init(){let t=await fetch(`${this.baseUrl}/sdk/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:this.apiKey})});if(!t.ok){let o=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${o.error??t.statusText}`)}let a=await t.json();if(!a.valid)throw new Error(`Gradual: Invalid API key - ${a.error??"Unknown error"}`);let e=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!e.ok){let o=await e.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${o.error??e.statusText}`)}this.snapshot=await e.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){let a={},e=new Set([...Object.keys(this.identifiedContext),...Object.keys(t?.context??{})]);for(let o of e)a[o]={...this.identifiedContext[o],...t?.context?.[o]};return a}evaluate(t,a){let e=this.ensureReady();if(!e.flags)return;let o=e.flags[t];if(o)return s(o,a,e.segments??{})}async ready(){await this.initPromise;}isReady(){return this.snapshot!==null}async isEnabled(t,a){await this.initPromise;let e=this.evaluate(t,this.mergeContext(a));return typeof e=="boolean"?e:false}async get(t,a){await this.initPromise;let e=this.evaluate(t,this.mergeContext(a));return e??a.fallback}isEnabledSync(t,a){let e=this.evaluate(t,this.mergeContext(a));return typeof e=="boolean"?e:false}getSync(t,a){let e=this.evaluate(t,this.mergeContext(a));return e??a.fallback}identify(t){this.identifiedContext={...t};}reset(){this.identifiedContext={};}async refresh(){let t=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!t.ok){let a=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to refresh - ${a.error??t.statusText}`)}this.snapshot=await t.json();}getSnapshot(){return this.snapshot}};function f(i){return new l(i)}
2
- exports.createGradual=f;exports.evaluateFlag=s;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';function p(e){let t=0;for(let n=0;n<e.length;n++){let a=e.charCodeAt(n);t=(t<<5)-t+a,t|=0;}return Math.abs(t)}function l(e,t,n){let a=t[n.bucketContextKind]?.[n.bucketAttributeKey],o=n.seed??"",r=`${e}:${o}:${String(a??"anonymous")}`;return p(r)%1e5}function d(e,t,n){let a=0;for(let r of e.variations)if(a+=r.weight,t<a)return n[r.variationKey];let o=e.variations.at(-1);return o?n[o.variationKey]:void 0}function c(e,t){let{contextKind:n,attributeKey:a,operator:o,value:r}=e,i=t[n]?.[a];switch(o){case "equals":return i===r;case "not_equals":return i!==r;case "contains":return typeof i=="string"&&typeof r=="string"||Array.isArray(i)?i.includes(r):false;case "not_contains":return typeof i=="string"&&typeof r=="string"||Array.isArray(i)?!i.includes(r):true;case "starts_with":return typeof i=="string"&&typeof r=="string"?i.startsWith(r):false;case "ends_with":return typeof i=="string"&&typeof r=="string"?i.endsWith(r):false;case "greater_than":return typeof i=="number"&&typeof r=="number"?i>r:false;case "less_than":return typeof i=="number"&&typeof r=="number"?i<r:false;case "greater_than_or_equal":return typeof i=="number"&&typeof r=="number"?i>=r:false;case "less_than_or_equal":return typeof i=="number"&&typeof r=="number"?i<=r:false;case "in":return Array.isArray(r)?r.includes(i):false;case "not_in":return Array.isArray(r)?!r.includes(i):true;case "exists":return i!=null;case "not_exists":return i==null;default:return false}}function h(e,t){return e.every(n=>c(n,t))}function f(e,t){return h(e.conditions,t)}function v(e,t,n){switch(e.type){case "individual":return e.contextKind&&e.attributeKey&&e.attributeValue!==void 0?t[e.contextKind]?.[e.attributeKey]===e.attributeValue:false;case "rule":return e.conditions?h(e.conditions,t):false;case "segment":if(e.segmentKey){let a=n[e.segmentKey];if(a)return f(a,t)}return false;default:return false}}function y(e,t,n,a){if(e.rollout){let o=l(t,n,e.rollout);return d(e.rollout,o,a)}if(e.variationKey)return a[e.variationKey]}function m(e,t){if(e.defaultRollout){let n=l(e.key,t,e.defaultRollout);return d(e.defaultRollout,n,e.variations)}if(e.defaultVariationKey)return e.variations[e.defaultVariationKey]}function s(e,t,n){if(!e.enabled)return e.variations[e.offVariationKey]?.value;let a=[...e.targets].sort((r,i)=>r.sortOrder-i.sortOrder);for(let r of a)if(v(r,t,n)){let i=y(r,e.key,t,e.variations);if(i)return i.value}return m(e,t)?.value}var g="https://worker.gradual.so/api/v1",u=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};updateListeners=new Set;sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??g,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};let n=t.polling?.enabled??true,a=t.polling?.intervalMs??1e4;n&&this.initPromise.then(()=>{setInterval(async()=>{try{let o=this.snapshot?.version;if(await this.refresh(),this.snapshot&&this.snapshot.version!==o)for(let r of this.updateListeners)r();}catch(o){console.warn("Gradual: Polling refresh failed",o);}},a);});}async init(){let t=await fetch(`${this.baseUrl}/sdk/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:this.apiKey})});if(!t.ok){let o=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${o.error??t.statusText}`)}let n=await t.json();if(!n.valid)throw new Error(`Gradual: Invalid API key - ${n.error??"Unknown error"}`);let a=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!a.ok){let o=await a.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${o.error??a.statusText}`)}this.snapshot=await a.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){let n={},a=new Set([...Object.keys(this.identifiedContext),...Object.keys(t?.context??{})]);for(let o of a)n[o]={...this.identifiedContext[o],...t?.context?.[o]};return n}evaluate(t,n){let a=this.ensureReady();if(!a.flags)return;let o=a.flags[t];if(o)return s(o,n,a.segments??{})}async ready(){await this.initPromise;}isReady(){return this.snapshot!==null}async isEnabled(t,n){await this.initPromise;let a=this.evaluate(t,this.mergeContext(n));return typeof a=="boolean"?a:false}async get(t,n){await this.initPromise;let a=this.evaluate(t,this.mergeContext(n));return a??n.fallback}isEnabledSync(t,n){let a=this.evaluate(t,this.mergeContext(n));return typeof a=="boolean"?a:false}getSync(t,n){let a=this.evaluate(t,this.mergeContext(n));return a??n.fallback}identify(t){this.identifiedContext={...t};}reset(){this.identifiedContext={};}async refresh(){let t=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!t.ok){let n=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to refresh - ${n.error??t.statusText}`)}this.snapshot=await t.json();}getSnapshot(){return this.snapshot}onUpdate(t){return this.updateListeners.add(t),()=>this.updateListeners.delete(t)}};function b(e){return new u(e)}
2
+ exports.createGradual=b;exports.evaluateFlag=s;//# sourceMappingURL=index.cjs.map
3
3
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/evaluator.ts","../src/client.ts"],"names":["evaluateCondition","condition","context","contextKind","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","evaluateFlag","flag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","pollingEnabled","pollingIntervalMs","error","initResponse","initData","snapshotResponse","merged","allKinds","kind","key","snapshot","response","createGradual"],"mappings":"aAQA,SAASA,CAAAA,CACPC,EACAC,CAAAA,CACS,CACT,GAAM,CAAE,WAAA,CAAAC,CAAAA,CAAa,YAAA,CAAAC,CAAAA,CAAc,QAAA,CAAAC,EAAU,KAAA,CAAAC,CAAM,CAAA,CAAIL,CAAAA,CACjDM,CAAAA,CAAeL,CAAAA,CAAQC,CAAW,CAAA,GAAIC,CAAY,CAAA,CAExD,OAAQC,CAAAA,EACN,KAAK,QAAA,CACH,OAAOE,IAAiBD,CAAAA,CAE1B,KAAK,aACH,OAAOC,CAAAA,GAAiBD,CAAAA,CAE1B,KAAK,UAAA,CAIH,OAHI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,EAGrD,KAAA,CAAM,QAAQC,CAAY,CAAA,CACrBA,CAAAA,CAAa,QAAA,CAASD,CAAK,CAAA,CAE7B,MAET,KAAK,cAAA,CAIH,OAHI,OAAOC,CAAAA,EAAiB,UAAY,OAAOD,CAAAA,EAAU,QAAA,EAGrD,KAAA,CAAM,OAAA,CAAQC,CAAY,EACrB,CAACA,CAAAA,CAAa,QAAA,CAASD,CAAK,CAAA,CAE9B,IAAA,CAET,KAAK,aAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,GAAU,QAAA,CAChDC,CAAAA,CAAa,WAAWD,CAAK,CAAA,CAE/B,MAET,KAAK,WAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,CAAa,QAAA,CAASD,CAAK,CAAA,CAE7B,MAET,KAAK,cAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,EAAeD,CAAAA,CAEjB,KAAA,CAET,KAAK,WAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,GAAU,QAAA,CAChDC,CAAAA,CAAeD,CAAAA,CAEjB,KAAA,CAET,KAAK,uBAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,GAAgBD,CAAAA,CAElB,KAAA,CAET,KAAK,oBAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,EAAgBD,EAElB,KAAA,CAET,KAAK,IAAA,CACH,OAAI,KAAA,CAAM,OAAA,CAAQA,CAAK,CAAA,CACdA,CAAAA,CAAM,QAAA,CAASC,CAAY,CAAA,CAE7B,KAAA,CAET,KAAK,QAAA,CACH,OAAI,MAAM,OAAA,CAAQD,CAAK,EACd,CAACA,CAAAA,CAAM,QAAA,CAASC,CAAY,CAAA,CAE9B,IAAA,CAET,KAAK,QAAA,CACH,OAAqCA,CAAAA,EAAiB,IAAA,CAExD,KAAK,YAAA,CACH,OAAqCA,CAAAA,EAAiB,IAAA,CAExD,QACE,OAAO,MACX,CACF,CAEA,SAASC,CAAAA,CACPC,CAAAA,CACAP,CAAAA,CACS,CACT,OAAOO,EAAW,KAAA,CAAOR,CAAAA,EAAcD,CAAAA,CAAkBC,CAAAA,CAAWC,CAAO,CAAC,CAC9E,CAEA,SAASQ,CAAAA,CACPC,CAAAA,CACAT,CAAAA,CACS,CACT,OAAOM,CAAAA,CAAmBG,CAAAA,CAAQ,UAAA,CAAYT,CAAO,CACvD,CAEA,SAASU,CAAAA,CACPC,CAAAA,CACAX,EACAY,CAAAA,CACS,CACT,OAAQD,CAAAA,CAAO,IAAA,EACb,KAAK,YAAA,CACH,OACEA,EAAO,WAAA,EACPA,CAAAA,CAAO,YAAA,EACPA,CAAAA,CAAO,cAAA,GAAmB,MAAA,CAGxBX,EAAQW,CAAAA,CAAO,WAAW,CAAA,GAAIA,CAAAA,CAAO,YAAY,CAAA,GACjDA,EAAO,cAAA,CAGJ,KAAA,CAET,KAAK,MAAA,CACH,OAAIA,EAAO,UAAA,CACFL,CAAAA,CAAmBK,CAAAA,CAAO,UAAA,CAAYX,CAAO,CAAA,CAE/C,MAET,KAAK,SAAA,CACH,GAAIW,CAAAA,CAAO,UAAA,CAAY,CACrB,IAAMF,CAAAA,CAAUG,CAAAA,CAASD,CAAAA,CAAO,UAAU,CAAA,CAC1C,GAAIF,EACF,OAAOD,CAAAA,CAAgBC,EAAST,CAAO,CAE3C,CACA,OAAO,MAAA,CAET,QACE,OAAO,MACX,CACF,CAEO,SAASa,CAAAA,CACdC,CAAAA,CACAd,CAAAA,CACAY,CAAAA,CACS,CACT,GAAI,CAACE,CAAAA,CAAK,OAAA,CAER,OADqBA,CAAAA,CAAK,UAAA,CAAWA,EAAK,eAAe,CAAA,EACpC,MAGvB,IAAMC,CAAAA,CAAgB,CAAC,GAAGD,CAAAA,CAAK,OAAO,CAAA,CAAE,IAAA,CACtC,CAACE,EAAGC,CAAAA,GAAMD,CAAAA,CAAE,SAAA,CAAYC,CAAAA,CAAE,SAC5B,CAAA,CAEA,QAAWN,CAAAA,IAAUI,CAAAA,CACnB,GAAIL,CAAAA,CAAeC,CAAAA,CAAQX,CAAAA,CAASY,CAAQ,CAAA,CAAG,CAC7C,IAAMM,CAAAA,CAAYJ,CAAAA,CAAK,WAAWH,CAAAA,CAAO,YAAY,CAAA,CACrD,GAAIO,CAAAA,CACF,OAAOA,EAAU,KAErB,CAIF,OADyBJ,CAAAA,CAAK,UAAA,CAAWA,CAAAA,CAAK,mBAAmB,CAAA,EACxC,KAC3B,CCxKA,IAAMK,CAAAA,CAAmB,kCAAA,CAuCnBC,EAAN,KAAuC,CACpB,OACA,WAAA,CACA,OAAA,CACA,YACT,QAAA,CAAuC,IAAA,CACvC,iBAAA,CAAuC,EAAC,CAEvC,IAAA,CAET,YAAYC,CAAAA,CAAyB,CACnC,IAAA,CAAK,MAAA,CAASA,CAAAA,CAAQ,MAAA,CACtB,KAAK,WAAA,CAAcA,CAAAA,CAAQ,WAAA,CAC3B,IAAA,CAAK,OAAA,CAAUA,CAAAA,CAAQ,SAAWF,CAAAA,CAClC,IAAA,CAAK,YAAc,IAAA,CAAK,IAAA,GAExB,IAAA,CAAK,IAAA,CAAO,CACV,SAAA,CAAW,IAAA,CAAK,aAAA,CAAc,KAAK,IAAI,CAAA,CACvC,GAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAC7B,CAAA,CAGA,IAAMG,CAAAA,CAAiBD,CAAAA,CAAQ,OAAA,EAAS,SAAW,IAAA,CAC7CE,CAAAA,CAAoBF,CAAAA,CAAQ,OAAA,EAAS,UAAA,EAAc,GAAA,CAGrDC,GACF,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAM,CAC1B,WAAA,CAAY,SAAY,CACtB,GAAI,CACF,MAAM,IAAA,CAAK,OAAA,GACb,CAAA,MAASE,CAAAA,CAAO,CAEd,OAAA,CAAQ,IAAA,CAAK,iCAAA,CAAmCA,CAAK,EACvD,CACF,EAAGD,CAAiB,EACtB,CAAC,EAEL,CAEA,MAAc,IAAA,EAAsB,CAClC,IAAME,EAAe,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,SAAA,CAAA,CAAa,CAC3D,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,EAC9C,IAAA,CAAM,IAAA,CAAK,UAAU,CAAE,MAAA,CAAQ,KAAK,MAAO,CAAC,CAC9C,CAAC,CAAA,CAED,GAAI,CAACA,CAAAA,CAAa,EAAA,CAAI,CACpB,IAAMD,CAAAA,CAAQ,MAAMC,EAAa,IAAA,EAAK,CAAE,KAAA,CAAM,KAAO,EAAC,CAAE,EACxD,MAAM,IAAI,MACR,CAAA,gCAAA,EAAoCD,CAAAA,CAA6B,OAASC,CAAAA,CAAa,UAAU,CAAA,CACnG,CACF,CAEA,IAAMC,EAAY,MAAMD,CAAAA,CAAa,IAAA,EAAK,CAK1C,GAAI,CAACC,EAAS,KAAA,CACZ,MAAM,IAAI,KAAA,CACR,CAAA,2BAAA,EAA8BA,CAAAA,CAAS,OAAS,eAAe,CAAA,CACjE,EAGF,IAAMC,CAAAA,CAAmB,MAAM,KAAA,CAC7B,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,0BAAA,EAA6B,kBAAA,CAAmB,KAAK,WAAW,CAAC,CAAA,CAAA,CAChF,CAAE,OAAA,CAAS,CAAE,cAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAG,CAAE,CACxD,EAEA,GAAI,CAACA,EAAiB,EAAA,CAAI,CACxB,IAAMH,CAAAA,CAAQ,MAAMG,CAAAA,CAAiB,IAAA,EAAK,CAAE,KAAA,CAAM,KAAO,EAAC,CAAE,CAAA,CAC5D,MAAM,IAAI,KAAA,CACR,uCAAwCH,CAAAA,CAA6B,KAAA,EAASG,CAAAA,CAAiB,UAAU,CAAA,CAC3G,CACF,CAEA,IAAA,CAAK,QAAA,CAAY,MAAMA,CAAAA,CAAiB,IAAA,GAC1C,CAEQ,WAAA,EAAmC,CACzC,GAAI,CAAC,IAAA,CAAK,SACR,MAAM,IAAI,KAAA,CACR,6DACF,CAAA,CAEF,OAAO,KAAK,QACd,CAEQ,YAAA,CAAaN,CAAAA,CAEC,CACpB,IAAMO,EAA4B,EAAC,CAC7BC,EAAW,IAAI,GAAA,CAAI,CACvB,GAAG,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,iBAAiB,CAAA,CACrC,GAAG,MAAA,CAAO,IAAA,CAAKR,CAAAA,EAAS,OAAA,EAAW,EAAE,CACvC,CAAC,CAAA,CACD,IAAA,IAAWS,CAAAA,IAAQD,CAAAA,CACjBD,CAAAA,CAAOE,CAAI,CAAA,CAAI,CACb,GAAG,IAAA,CAAK,iBAAA,CAAkBA,CAAI,EAC9B,GAAGT,CAAAA,EAAS,OAAA,GAAUS,CAAI,CAC5B,CAAA,CAEF,OAAOF,CACT,CAEQ,QAAA,CAASG,CAAAA,CAAa/B,CAAAA,CAAqC,CACjE,IAAMgC,CAAAA,CAAW,IAAA,CAAK,WAAA,EAAY,CAClC,GAAI,CAACA,EAAS,KAAA,CACZ,OAEF,IAAMlB,CAAAA,CAAOkB,CAAAA,CAAS,MAAMD,CAAG,CAAA,CAC/B,GAAKjB,CAAAA,CAGL,OAAOD,CAAAA,CAAaC,EAAMd,CAAAA,CAASgC,CAAAA,CAAS,QAAA,EAAY,EAAE,CAC5D,CAEA,MAAM,KAAA,EAAuB,CAC3B,MAAM,IAAA,CAAK,YACb,CAEA,OAAA,EAAmB,CACjB,OAAO,IAAA,CAAK,QAAA,GAAa,IAC3B,CAEA,MAAM,SAAA,CAAUD,CAAAA,CAAaV,CAAAA,CAA8C,CACzE,MAAM,IAAA,CAAK,WAAA,CACX,IAAMjB,CAAAA,CAAQ,IAAA,CAAK,QAAA,CAAS2B,EAAK,IAAA,CAAK,YAAA,CAAaV,CAAO,CAAC,CAAA,CAC3D,OAAO,OAAOjB,CAAAA,EAAU,SAAA,CAAYA,EAAQ,KAC9C,CAEA,MAAM,GAAA,CAAO2B,CAAAA,CAAaV,CAAAA,CAAqC,CAC7D,MAAM,IAAA,CAAK,YACX,IAAMjB,CAAAA,CAAQ,IAAA,CAAK,QAAA,CAAS2B,CAAAA,CAAK,IAAA,CAAK,aAAaV,CAAO,CAAC,CAAA,CAC3D,OAA8BjB,CAAAA,EAE1BiB,CAAAA,CAAQ,QACd,CAEQ,aAAA,CAAcU,CAAAA,CAAaV,CAAAA,CAAqC,CACtE,IAAMjB,EAAQ,IAAA,CAAK,QAAA,CAAS2B,CAAAA,CAAK,IAAA,CAAK,YAAA,CAAaV,CAAO,CAAC,CAAA,CAC3D,OAAO,OAAOjB,CAAAA,EAAU,SAAA,CAAYA,CAAAA,CAAQ,KAC9C,CAEQ,OAAA,CAAW2B,CAAAA,CAAaV,CAAAA,CAA4B,CAC1D,IAAMjB,EAAQ,IAAA,CAAK,QAAA,CAAS2B,EAAK,IAAA,CAAK,YAAA,CAAaV,CAAO,CAAC,CAAA,CAC3D,OAA8BjB,CAAAA,EAE1BiB,CAAAA,CAAQ,QACd,CAEA,QAAA,CAASrB,CAAAA,CAAkC,CACzC,IAAA,CAAK,iBAAA,CAAoB,CAAE,GAAGA,CAAQ,EACxC,CAEA,KAAA,EAAc,CACZ,IAAA,CAAK,kBAAoB,GAC3B,CAEA,MAAM,OAAA,EAAyB,CAC7B,IAAMiC,CAAAA,CAAW,MAAM,KAAA,CACrB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,0BAAA,EAA6B,kBAAA,CAAmB,IAAA,CAAK,WAAW,CAAC,CAAA,CAAA,CAChF,CAAE,OAAA,CAAS,CAAE,aAAA,CAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,EAAG,CAAE,CACxD,EAEA,GAAI,CAACA,EAAS,EAAA,CAAI,CAChB,IAAMT,CAAAA,CAAQ,MAAMS,CAAAA,CAAS,MAAK,CAAE,KAAA,CAAM,KAAO,EAAC,CAAE,CAAA,CACpD,MAAM,IAAI,KAAA,CACR,CAAA,6BAAA,EAAiCT,CAAAA,CAA6B,KAAA,EAASS,CAAAA,CAAS,UAAU,CAAA,CAC5F,CACF,CAEA,IAAA,CAAK,QAAA,CAAY,MAAMA,EAAS,IAAA,GAClC,CAEA,WAAA,EAA0C,CACxC,OAAO,KAAK,QACd,CACF,CAAA,CAuBO,SAASC,CAAAA,CAAcb,CAAAA,CAAkC,CAC9D,OAAO,IAAID,CAAAA,CAAcC,CAAO,CAClC","file":"index.cjs","sourcesContent":["import type {\n EvaluationContext,\n SnapshotFlag,\n SnapshotRuleCondition,\n SnapshotSegment,\n SnapshotTarget,\n} from \"./types\";\n\nfunction evaluateCondition(\n condition: SnapshotRuleCondition,\n context: EvaluationContext\n): boolean {\n const { contextKind, attributeKey, operator, value } = condition;\n const contextValue = context[contextKind]?.[attributeKey];\n\n switch (operator) {\n case \"equals\":\n return contextValue === value;\n\n case \"not_equals\":\n return contextValue !== value;\n\n case \"contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return contextValue.includes(value);\n }\n return false;\n\n case \"not_contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return !contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return !contextValue.includes(value);\n }\n return true;\n\n case \"starts_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.startsWith(value);\n }\n return false;\n\n case \"ends_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.endsWith(value);\n }\n return false;\n\n case \"greater_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue > value;\n }\n return false;\n\n case \"less_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue < value;\n }\n return false;\n\n case \"greater_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue >= value;\n }\n return false;\n\n case \"less_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue <= value;\n }\n return false;\n\n case \"in\":\n if (Array.isArray(value)) {\n return value.includes(contextValue);\n }\n return false;\n\n case \"not_in\":\n if (Array.isArray(value)) {\n return !value.includes(contextValue);\n }\n return true;\n\n case \"exists\":\n return contextValue !== undefined && contextValue !== null;\n\n case \"not_exists\":\n return contextValue === undefined || contextValue === null;\n\n default:\n return false;\n }\n}\n\nfunction evaluateConditions(\n conditions: SnapshotRuleCondition[],\n context: EvaluationContext\n): boolean {\n return conditions.every((condition) => evaluateCondition(condition, context));\n}\n\nfunction evaluateSegment(\n segment: SnapshotSegment,\n context: EvaluationContext\n): boolean {\n return evaluateConditions(segment.conditions, context);\n}\n\nfunction evaluateTarget(\n target: SnapshotTarget,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): boolean {\n switch (target.type) {\n case \"individual\":\n if (\n target.contextKind &&\n target.attributeKey &&\n target.attributeValue !== undefined\n ) {\n return (\n context[target.contextKind]?.[target.attributeKey] ===\n target.attributeValue\n );\n }\n return false;\n\n case \"rule\":\n if (target.conditions) {\n return evaluateConditions(target.conditions, context);\n }\n return false;\n\n case \"segment\":\n if (target.segmentKey) {\n const segment = segments[target.segmentKey];\n if (segment) {\n return evaluateSegment(segment, context);\n }\n }\n return false;\n\n default:\n return false;\n }\n}\n\nexport function evaluateFlag(\n flag: SnapshotFlag,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): unknown {\n if (!flag.enabled) {\n const offVariation = flag.variations[flag.offVariationKey];\n return offVariation?.value;\n }\n\n const sortedTargets = [...flag.targets].sort(\n (a, b) => a.sortOrder - b.sortOrder\n );\n\n for (const target of sortedTargets) {\n if (evaluateTarget(target, context, segments)) {\n const variation = flag.variations[target.variationKey];\n if (variation) {\n return variation.value;\n }\n }\n }\n\n const defaultVariation = flag.variations[flag.defaultVariationKey];\n return defaultVariation?.value;\n}\n","import { evaluateFlag } from \"./evaluator\";\nimport type {\n EnvironmentSnapshot,\n EvaluationContext,\n FlagOptions,\n GradualOptions,\n IsEnabledOptions,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://worker.gradual.so/api/v1\";\n\nexport interface Gradual {\n /** Wait for the SDK to be ready (snapshot fetched) */\n ready(): Promise<void>;\n\n /** Check if the SDK is ready for sync access */\n isReady(): boolean;\n\n /** Check if a boolean flag is enabled */\n isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean>;\n\n /** Get a flag value with type inference from fallback */\n get<T>(key: string, options: FlagOptions<T>): Promise<T>;\n\n /** Set persistent user context for all evaluations */\n identify(context: EvaluationContext): void;\n\n /** Clear the identified user context */\n reset(): void;\n\n /** Refresh the snapshot from the server */\n refresh(): Promise<void>;\n\n /** Get the current snapshot (for debugging) */\n getSnapshot(): EnvironmentSnapshot | null;\n\n /** Sync methods (throw if not ready) */\n sync: GradualSync;\n}\n\nexport interface GradualSync {\n /** Sync version of isEnabled (throws if not ready) */\n isEnabled(key: string, options?: IsEnabledOptions): boolean;\n\n /** Sync version of get (throws if not ready) */\n get<T>(key: string, options: FlagOptions<T>): T;\n}\n\nclass GradualClient implements Gradual {\n private readonly apiKey: string;\n private readonly environment: string;\n private readonly baseUrl: string;\n private readonly initPromise: Promise<void>;\n private snapshot: EnvironmentSnapshot | null = null;\n private identifiedContext: EvaluationContext = {};\n\n readonly sync: GradualSync;\n\n constructor(options: GradualOptions) {\n this.apiKey = options.apiKey;\n this.environment = options.environment;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n this.initPromise = this.init();\n\n this.sync = {\n isEnabled: this.isEnabledSync.bind(this),\n get: this.getSync.bind(this),\n };\n\n // Polling defaults: enabled=true, interval=10000ms\n const pollingEnabled = options.polling?.enabled ?? true;\n const pollingIntervalMs = options.polling?.intervalMs ?? 10_000;\n\n // Start polling after init if enabled\n if (pollingEnabled) {\n this.initPromise.then(() => {\n setInterval(async () => {\n try {\n await this.refresh();\n } catch (error) {\n // Silent fail - don't crash the app\n console.warn(\"Gradual: Polling refresh failed\", error);\n }\n }, pollingIntervalMs);\n });\n }\n }\n\n private async init(): Promise<void> {\n const initResponse = await fetch(`${this.baseUrl}/sdk/init`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ apiKey: this.apiKey }),\n });\n\n if (!initResponse.ok) {\n const error = await initResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to initialize - ${(error as { error?: string }).error ?? initResponse.statusText}`\n );\n }\n\n const initData = (await initResponse.json()) as {\n valid: boolean;\n error?: string;\n };\n\n if (!initData.valid) {\n throw new Error(\n `Gradual: Invalid API key - ${initData.error ?? \"Unknown error\"}`\n );\n }\n\n const snapshotResponse = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!snapshotResponse.ok) {\n const error = await snapshotResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to fetch snapshot - ${(error as { error?: string }).error ?? snapshotResponse.statusText}`\n );\n }\n\n this.snapshot = (await snapshotResponse.json()) as EnvironmentSnapshot;\n }\n\n private ensureReady(): EnvironmentSnapshot {\n if (!this.snapshot) {\n throw new Error(\n \"Gradual: SDK not ready. Use await ready() or async methods.\"\n );\n }\n return this.snapshot;\n }\n\n private mergeContext(options?: {\n context?: EvaluationContext;\n }): EvaluationContext {\n const merged: EvaluationContext = {};\n const allKinds = new Set([\n ...Object.keys(this.identifiedContext),\n ...Object.keys(options?.context ?? {}),\n ]);\n for (const kind of allKinds) {\n merged[kind] = {\n ...this.identifiedContext[kind],\n ...options?.context?.[kind],\n };\n }\n return merged;\n }\n\n private evaluate(key: string, context: EvaluationContext): unknown {\n const snapshot = this.ensureReady();\n if (!snapshot.flags) {\n return undefined;\n }\n const flag = snapshot.flags[key];\n if (!flag) {\n return undefined;\n }\n return evaluateFlag(flag, context, snapshot.segments ?? {});\n }\n\n async ready(): Promise<void> {\n await this.initPromise;\n }\n\n isReady(): boolean {\n return this.snapshot !== null;\n }\n\n async isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n async get<T>(key: string, options: FlagOptions<T>): Promise<T> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n private isEnabledSync(key: string, options?: IsEnabledOptions): boolean {\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n private getSync<T>(key: string, options: FlagOptions<T>): T {\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n identify(context: EvaluationContext): void {\n this.identifiedContext = { ...context };\n }\n\n reset(): void {\n this.identifiedContext = {};\n }\n\n async refresh(): Promise<void> {\n const response = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to refresh - ${(error as { error?: string }).error ?? response.statusText}`\n );\n }\n\n this.snapshot = (await response.json()) as EnvironmentSnapshot;\n }\n\n getSnapshot(): EnvironmentSnapshot | null {\n return this.snapshot;\n }\n}\n\n/**\n * Create a Gradual feature flag client\n *\n * @example\n * ```ts\n * const gradual = createGradual({\n * apiKey: 'gra_xxx',\n * environment: 'production'\n * })\n *\n * // Boolean flags\n * const enabled = await gradual.isEnabled('new-feature')\n *\n * // Typed values (inferred from fallback)\n * const theme = await gradual.get('theme', { fallback: 'dark' })\n *\n * // With user context\n * gradual.identify({ userId: '123', plan: 'pro' })\n * const proFeature = await gradual.isEnabled('pro-feature')\n * ```\n */\nexport function createGradual(options: GradualOptions): Gradual {\n return new GradualClient(options);\n}\n"]}
1
+ {"version":3,"sources":["../src/evaluator.ts","../src/client.ts"],"names":["hashString","str","hash","i","char","getBucketValue","flagKey","context","rollout","bucketKey","seed","hashInput","selectVariationFromRollout","bucketValue","variations","cumulative","rv","lastVariation","evaluateCondition","condition","contextKind","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","resolveTargetVariation","resolveDefaultVariation","flag","evaluateFlag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","pollingEnabled","pollingIntervalMs","previousVersion","cb","error","initResponse","initData","snapshotResponse","merged","allKinds","kind","key","snapshot","response","callback","createGradual"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n EvaluationContext,\n SnapshotFlag,\n SnapshotRollout,\n SnapshotRuleCondition,\n SnapshotSegment,\n SnapshotTarget,\n SnapshotVariation,\n} from \"./types\";\n\nfunction hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n // biome-ignore lint/suspicious/noBitwiseOperators: intentional hash algorithm using bitwise shift\n hash = (hash << 5) - hash + char;\n // biome-ignore lint/suspicious/noBitwiseOperators: converting to 32-bit integer\n hash |= 0;\n }\n return Math.abs(hash);\n}\n\nfunction getBucketValue(\n flagKey: string,\n context: EvaluationContext,\n rollout: SnapshotRollout\n): number {\n const bucketKey =\n context[rollout.bucketContextKind]?.[rollout.bucketAttributeKey];\n const seed = rollout.seed ?? \"\";\n const hashInput = `${flagKey}:${seed}:${String(bucketKey ?? \"anonymous\")}`;\n return hashString(hashInput) % 100_000;\n}\n\nfunction selectVariationFromRollout(\n rollout: SnapshotRollout,\n bucketValue: number,\n variations: Record<string, SnapshotVariation>\n): SnapshotVariation | undefined {\n let cumulative = 0;\n\n for (const rv of rollout.variations) {\n cumulative += rv.weight;\n if (bucketValue < cumulative) {\n return variations[rv.variationKey];\n }\n }\n\n const lastVariation = rollout.variations.at(-1);\n return lastVariation ? variations[lastVariation.variationKey] : undefined;\n}\n\nfunction evaluateCondition(\n condition: SnapshotRuleCondition,\n context: EvaluationContext\n): boolean {\n const { contextKind, attributeKey, operator, value } = condition;\n const contextValue = context[contextKind]?.[attributeKey];\n\n switch (operator) {\n case \"equals\":\n return contextValue === value;\n\n case \"not_equals\":\n return contextValue !== value;\n\n case \"contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return contextValue.includes(value);\n }\n return false;\n\n case \"not_contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return !contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return !contextValue.includes(value);\n }\n return true;\n\n case \"starts_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.startsWith(value);\n }\n return false;\n\n case \"ends_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.endsWith(value);\n }\n return false;\n\n case \"greater_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue > value;\n }\n return false;\n\n case \"less_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue < value;\n }\n return false;\n\n case \"greater_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue >= value;\n }\n return false;\n\n case \"less_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue <= value;\n }\n return false;\n\n case \"in\":\n if (Array.isArray(value)) {\n return value.includes(contextValue);\n }\n return false;\n\n case \"not_in\":\n if (Array.isArray(value)) {\n return !value.includes(contextValue);\n }\n return true;\n\n case \"exists\":\n return contextValue !== undefined && contextValue !== null;\n\n case \"not_exists\":\n return contextValue === undefined || contextValue === null;\n\n default:\n return false;\n }\n}\n\nfunction evaluateConditions(\n conditions: SnapshotRuleCondition[],\n context: EvaluationContext\n): boolean {\n return conditions.every((condition) => evaluateCondition(condition, context));\n}\n\nfunction evaluateSegment(\n segment: SnapshotSegment,\n context: EvaluationContext\n): boolean {\n return evaluateConditions(segment.conditions, context);\n}\n\nfunction evaluateTarget(\n target: SnapshotTarget,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): boolean {\n switch (target.type) {\n case \"individual\":\n if (\n target.contextKind &&\n target.attributeKey &&\n target.attributeValue !== undefined\n ) {\n return (\n context[target.contextKind]?.[target.attributeKey] ===\n target.attributeValue\n );\n }\n return false;\n\n case \"rule\":\n if (target.conditions) {\n return evaluateConditions(target.conditions, context);\n }\n return false;\n\n case \"segment\":\n if (target.segmentKey) {\n const segment = segments[target.segmentKey];\n if (segment) {\n return evaluateSegment(segment, context);\n }\n }\n return false;\n\n default:\n return false;\n }\n}\n\nfunction resolveTargetVariation(\n target: SnapshotTarget,\n flagKey: string,\n context: EvaluationContext,\n variations: Record<string, SnapshotVariation>\n): SnapshotVariation | undefined {\n if (target.rollout) {\n const bucketValue = getBucketValue(flagKey, context, target.rollout);\n return selectVariationFromRollout(target.rollout, bucketValue, variations);\n }\n\n if (target.variationKey) {\n return variations[target.variationKey];\n }\n\n return undefined;\n}\n\nfunction resolveDefaultVariation(\n flag: SnapshotFlag,\n context: EvaluationContext\n): SnapshotVariation | undefined {\n if (flag.defaultRollout) {\n const bucketValue = getBucketValue(flag.key, context, flag.defaultRollout);\n return selectVariationFromRollout(\n flag.defaultRollout,\n bucketValue,\n flag.variations\n );\n }\n\n if (flag.defaultVariationKey) {\n return flag.variations[flag.defaultVariationKey];\n }\n\n return undefined;\n}\n\nexport function evaluateFlag(\n flag: SnapshotFlag,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): unknown {\n if (!flag.enabled) {\n const offVariation = flag.variations[flag.offVariationKey];\n return offVariation?.value;\n }\n\n const sortedTargets = [...flag.targets].sort(\n (a, b) => a.sortOrder - b.sortOrder\n );\n\n for (const target of sortedTargets) {\n if (evaluateTarget(target, context, segments)) {\n const variation = resolveTargetVariation(\n target,\n flag.key,\n context,\n flag.variations\n );\n if (variation) {\n return variation.value;\n }\n }\n }\n\n const defaultVariation = resolveDefaultVariation(flag, context);\n return defaultVariation?.value;\n}\n","import { evaluateFlag } from \"./evaluator\";\nimport type {\n EnvironmentSnapshot,\n EvaluationContext,\n FlagOptions,\n GradualOptions,\n IsEnabledOptions,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://worker.gradual.so/api/v1\";\n\nexport interface Gradual {\n /** Wait for the SDK to be ready (snapshot fetched) */\n ready(): Promise<void>;\n\n /** Check if the SDK is ready for sync access */\n isReady(): boolean;\n\n /** Check if a boolean flag is enabled */\n isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean>;\n\n /** Get a flag value with type inference from fallback */\n get<T>(key: string, options: FlagOptions<T>): Promise<T>;\n\n /** Set persistent user context for all evaluations */\n identify(context: EvaluationContext): void;\n\n /** Clear the identified user context */\n reset(): void;\n\n /** Refresh the snapshot from the server */\n refresh(): Promise<void>;\n\n /** Get the current snapshot (for debugging) */\n getSnapshot(): EnvironmentSnapshot | null;\n\n /** Subscribe to snapshot updates from polling (returns unsubscribe function) */\n onUpdate(callback: () => void): () => void;\n\n /** Sync methods (throw if not ready) */\n sync: GradualSync;\n}\n\nexport interface GradualSync {\n /** Sync version of isEnabled (throws if not ready) */\n isEnabled(key: string, options?: IsEnabledOptions): boolean;\n\n /** Sync version of get (throws if not ready) */\n get<T>(key: string, options: FlagOptions<T>): T;\n}\n\nclass GradualClient implements Gradual {\n private readonly apiKey: string;\n private readonly environment: string;\n private readonly baseUrl: string;\n private readonly initPromise: Promise<void>;\n private snapshot: EnvironmentSnapshot | null = null;\n private identifiedContext: EvaluationContext = {};\n private readonly updateListeners: Set<() => void> = new Set();\n\n readonly sync: GradualSync;\n\n constructor(options: GradualOptions) {\n this.apiKey = options.apiKey;\n this.environment = options.environment;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n this.initPromise = this.init();\n\n this.sync = {\n isEnabled: this.isEnabledSync.bind(this),\n get: this.getSync.bind(this),\n };\n\n const pollingEnabled = options.polling?.enabled ?? true;\n const pollingIntervalMs = options.polling?.intervalMs ?? 10_000;\n\n if (pollingEnabled) {\n this.initPromise.then(() => {\n setInterval(async () => {\n try {\n const previousVersion = this.snapshot?.version;\n await this.refresh();\n if (this.snapshot && this.snapshot.version !== previousVersion) {\n for (const cb of this.updateListeners) {\n cb();\n }\n }\n } catch (error) {\n console.warn(\"Gradual: Polling refresh failed\", error);\n }\n }, pollingIntervalMs);\n });\n }\n }\n\n private async init(): Promise<void> {\n const initResponse = await fetch(`${this.baseUrl}/sdk/init`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ apiKey: this.apiKey }),\n });\n\n if (!initResponse.ok) {\n const error = await initResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to initialize - ${(error as { error?: string }).error ?? initResponse.statusText}`\n );\n }\n\n const initData = (await initResponse.json()) as {\n valid: boolean;\n error?: string;\n };\n\n if (!initData.valid) {\n throw new Error(\n `Gradual: Invalid API key - ${initData.error ?? \"Unknown error\"}`\n );\n }\n\n const snapshotResponse = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!snapshotResponse.ok) {\n const error = await snapshotResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to fetch snapshot - ${(error as { error?: string }).error ?? snapshotResponse.statusText}`\n );\n }\n\n this.snapshot = (await snapshotResponse.json()) as EnvironmentSnapshot;\n }\n\n private ensureReady(): EnvironmentSnapshot {\n if (!this.snapshot) {\n throw new Error(\n \"Gradual: SDK not ready. Use await ready() or async methods.\"\n );\n }\n return this.snapshot;\n }\n\n private mergeContext(options?: {\n context?: EvaluationContext;\n }): EvaluationContext {\n const merged: EvaluationContext = {};\n const allKinds = new Set([\n ...Object.keys(this.identifiedContext),\n ...Object.keys(options?.context ?? {}),\n ]);\n for (const kind of allKinds) {\n merged[kind] = {\n ...this.identifiedContext[kind],\n ...options?.context?.[kind],\n };\n }\n return merged;\n }\n\n private evaluate(key: string, context: EvaluationContext): unknown {\n const snapshot = this.ensureReady();\n if (!snapshot.flags) {\n return undefined;\n }\n const flag = snapshot.flags[key];\n if (!flag) {\n return undefined;\n }\n return evaluateFlag(flag, context, snapshot.segments ?? {});\n }\n\n async ready(): Promise<void> {\n await this.initPromise;\n }\n\n isReady(): boolean {\n return this.snapshot !== null;\n }\n\n async isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n async get<T>(key: string, options: FlagOptions<T>): Promise<T> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n private isEnabledSync(key: string, options?: IsEnabledOptions): boolean {\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n private getSync<T>(key: string, options: FlagOptions<T>): T {\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n identify(context: EvaluationContext): void {\n this.identifiedContext = { ...context };\n }\n\n reset(): void {\n this.identifiedContext = {};\n }\n\n async refresh(): Promise<void> {\n const response = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to refresh - ${(error as { error?: string }).error ?? response.statusText}`\n );\n }\n\n this.snapshot = (await response.json()) as EnvironmentSnapshot;\n }\n\n getSnapshot(): EnvironmentSnapshot | null {\n return this.snapshot;\n }\n\n onUpdate(callback: () => void): () => void {\n this.updateListeners.add(callback);\n return () => this.updateListeners.delete(callback);\n }\n}\n\n/**\n * Create a Gradual feature flag client\n *\n * @example\n * ```ts\n * const gradual = createGradual({\n * apiKey: 'gra_xxx',\n * environment: 'production'\n * })\n *\n * // Boolean flags\n * const enabled = await gradual.isEnabled('new-feature')\n *\n * // Typed values (inferred from fallback)\n * const theme = await gradual.get('theme', { fallback: 'dark' })\n *\n * // With user context\n * gradual.identify({ userId: '123', plan: 'pro' })\n * const proFeature = await gradual.isEnabled('pro-feature')\n * ```\n */\nexport function createGradual(options: GradualOptions): Gradual {\n return new GradualClient(options);\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -9,10 +9,21 @@ interface SnapshotSegment {
9
9
  key: string;
10
10
  conditions: SnapshotRuleCondition[];
11
11
  }
12
+ interface SnapshotRolloutVariation {
13
+ variationKey: string;
14
+ weight: number;
15
+ }
16
+ interface SnapshotRollout {
17
+ variations: SnapshotRolloutVariation[];
18
+ bucketContextKind: string;
19
+ bucketAttributeKey: string;
20
+ seed?: string;
21
+ }
12
22
  interface SnapshotTarget {
13
23
  type: "rule" | "individual" | "segment";
14
- variationKey: string;
15
24
  sortOrder: number;
25
+ variationKey?: string;
26
+ rollout?: SnapshotRollout;
16
27
  conditions?: SnapshotRuleCondition[];
17
28
  contextKind?: string;
18
29
  attributeKey?: string;
@@ -28,9 +39,10 @@ interface SnapshotFlag {
28
39
  type: "boolean" | "string" | "number" | "json";
29
40
  enabled: boolean;
30
41
  variations: Record<string, SnapshotVariation>;
31
- defaultVariationKey: string;
32
42
  offVariationKey: string;
33
43
  targets: SnapshotTarget[];
44
+ defaultVariationKey?: string;
45
+ defaultRollout?: SnapshotRollout;
34
46
  }
35
47
  interface EnvironmentSnapshot {
36
48
  version: number;
@@ -82,6 +94,8 @@ interface Gradual {
82
94
  refresh(): Promise<void>;
83
95
  /** Get the current snapshot (for debugging) */
84
96
  getSnapshot(): EnvironmentSnapshot | null;
97
+ /** Subscribe to snapshot updates from polling (returns unsubscribe function) */
98
+ onUpdate(callback: () => void): () => void;
85
99
  /** Sync methods (throw if not ready) */
86
100
  sync: GradualSync;
87
101
  }
@@ -116,4 +130,4 @@ declare function createGradual(options: GradualOptions): Gradual;
116
130
 
117
131
  declare function evaluateFlag(flag: SnapshotFlag, context: EvaluationContext, segments: Record<string, SnapshotSegment>): unknown;
118
132
 
119
- export { type EnvironmentSnapshot, type EvaluationContext, type FlagOptions, type Gradual, type GradualOptions, type GradualSync, type IsEnabledOptions, type SnapshotFlag, type SnapshotRuleCondition, type SnapshotSegment, type SnapshotTarget, type SnapshotVariation, type TargetingOperator, createGradual, evaluateFlag };
133
+ export { type EnvironmentSnapshot, type EvaluationContext, type FlagOptions, type Gradual, type GradualOptions, type GradualSync, type IsEnabledOptions, type PollingOptions, type SnapshotFlag, type SnapshotRollout, type SnapshotRolloutVariation, type SnapshotRuleCondition, type SnapshotSegment, type SnapshotTarget, type SnapshotVariation, type TargetingOperator, createGradual, evaluateFlag };
package/dist/index.d.ts CHANGED
@@ -9,10 +9,21 @@ interface SnapshotSegment {
9
9
  key: string;
10
10
  conditions: SnapshotRuleCondition[];
11
11
  }
12
+ interface SnapshotRolloutVariation {
13
+ variationKey: string;
14
+ weight: number;
15
+ }
16
+ interface SnapshotRollout {
17
+ variations: SnapshotRolloutVariation[];
18
+ bucketContextKind: string;
19
+ bucketAttributeKey: string;
20
+ seed?: string;
21
+ }
12
22
  interface SnapshotTarget {
13
23
  type: "rule" | "individual" | "segment";
14
- variationKey: string;
15
24
  sortOrder: number;
25
+ variationKey?: string;
26
+ rollout?: SnapshotRollout;
16
27
  conditions?: SnapshotRuleCondition[];
17
28
  contextKind?: string;
18
29
  attributeKey?: string;
@@ -28,9 +39,10 @@ interface SnapshotFlag {
28
39
  type: "boolean" | "string" | "number" | "json";
29
40
  enabled: boolean;
30
41
  variations: Record<string, SnapshotVariation>;
31
- defaultVariationKey: string;
32
42
  offVariationKey: string;
33
43
  targets: SnapshotTarget[];
44
+ defaultVariationKey?: string;
45
+ defaultRollout?: SnapshotRollout;
34
46
  }
35
47
  interface EnvironmentSnapshot {
36
48
  version: number;
@@ -82,6 +94,8 @@ interface Gradual {
82
94
  refresh(): Promise<void>;
83
95
  /** Get the current snapshot (for debugging) */
84
96
  getSnapshot(): EnvironmentSnapshot | null;
97
+ /** Subscribe to snapshot updates from polling (returns unsubscribe function) */
98
+ onUpdate(callback: () => void): () => void;
85
99
  /** Sync methods (throw if not ready) */
86
100
  sync: GradualSync;
87
101
  }
@@ -116,4 +130,4 @@ declare function createGradual(options: GradualOptions): Gradual;
116
130
 
117
131
  declare function evaluateFlag(flag: SnapshotFlag, context: EvaluationContext, segments: Record<string, SnapshotSegment>): unknown;
118
132
 
119
- export { type EnvironmentSnapshot, type EvaluationContext, type FlagOptions, type Gradual, type GradualOptions, type GradualSync, type IsEnabledOptions, type SnapshotFlag, type SnapshotRuleCondition, type SnapshotSegment, type SnapshotTarget, type SnapshotVariation, type TargetingOperator, createGradual, evaluateFlag };
133
+ export { type EnvironmentSnapshot, type EvaluationContext, type FlagOptions, type Gradual, type GradualOptions, type GradualSync, type IsEnabledOptions, type PollingOptions, type SnapshotFlag, type SnapshotRollout, type SnapshotRolloutVariation, type SnapshotRuleCondition, type SnapshotSegment, type SnapshotTarget, type SnapshotVariation, type TargetingOperator, createGradual, evaluateFlag };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- function d(i,t){let{contextKind:a,attributeKey:e,operator:o,value:r}=i,n=t[a]?.[e];switch(o){case "equals":return n===r;case "not_equals":return n!==r;case "contains":return typeof n=="string"&&typeof r=="string"||Array.isArray(n)?n.includes(r):false;case "not_contains":return typeof n=="string"&&typeof r=="string"||Array.isArray(n)?!n.includes(r):true;case "starts_with":return typeof n=="string"&&typeof r=="string"?n.startsWith(r):false;case "ends_with":return typeof n=="string"&&typeof r=="string"?n.endsWith(r):false;case "greater_than":return typeof n=="number"&&typeof r=="number"?n>r:false;case "less_than":return typeof n=="number"&&typeof r=="number"?n<r:false;case "greater_than_or_equal":return typeof n=="number"&&typeof r=="number"?n>=r:false;case "less_than_or_equal":return typeof n=="number"&&typeof r=="number"?n<=r:false;case "in":return Array.isArray(r)?r.includes(n):false;case "not_in":return Array.isArray(r)?!r.includes(n):true;case "exists":return n!=null;case "not_exists":return n==null;default:return false}}function u(i,t){return i.every(a=>d(a,t))}function p(i,t){return u(i.conditions,t)}function h(i,t,a){switch(i.type){case "individual":return i.contextKind&&i.attributeKey&&i.attributeValue!==void 0?t[i.contextKind]?.[i.attributeKey]===i.attributeValue:false;case "rule":return i.conditions?u(i.conditions,t):false;case "segment":if(i.segmentKey){let e=a[i.segmentKey];if(e)return p(e,t)}return false;default:return false}}function s(i,t,a){if(!i.enabled)return i.variations[i.offVariationKey]?.value;let e=[...i.targets].sort((r,n)=>r.sortOrder-n.sortOrder);for(let r of e)if(h(r,t,a)){let n=i.variations[r.variationKey];if(n)return n.value}return i.variations[i.defaultVariationKey]?.value}var c="https://worker.gradual.so/api/v1",l=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??c,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};let a=t.polling?.enabled??true,e=t.polling?.intervalMs??1e4;a&&this.initPromise.then(()=>{setInterval(async()=>{try{await this.refresh();}catch(o){console.warn("Gradual: Polling refresh failed",o);}},e);});}async init(){let t=await fetch(`${this.baseUrl}/sdk/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:this.apiKey})});if(!t.ok){let o=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${o.error??t.statusText}`)}let a=await t.json();if(!a.valid)throw new Error(`Gradual: Invalid API key - ${a.error??"Unknown error"}`);let e=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!e.ok){let o=await e.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${o.error??e.statusText}`)}this.snapshot=await e.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){let a={},e=new Set([...Object.keys(this.identifiedContext),...Object.keys(t?.context??{})]);for(let o of e)a[o]={...this.identifiedContext[o],...t?.context?.[o]};return a}evaluate(t,a){let e=this.ensureReady();if(!e.flags)return;let o=e.flags[t];if(o)return s(o,a,e.segments??{})}async ready(){await this.initPromise;}isReady(){return this.snapshot!==null}async isEnabled(t,a){await this.initPromise;let e=this.evaluate(t,this.mergeContext(a));return typeof e=="boolean"?e:false}async get(t,a){await this.initPromise;let e=this.evaluate(t,this.mergeContext(a));return e??a.fallback}isEnabledSync(t,a){let e=this.evaluate(t,this.mergeContext(a));return typeof e=="boolean"?e:false}getSync(t,a){let e=this.evaluate(t,this.mergeContext(a));return e??a.fallback}identify(t){this.identifiedContext={...t};}reset(){this.identifiedContext={};}async refresh(){let t=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!t.ok){let a=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to refresh - ${a.error??t.statusText}`)}this.snapshot=await t.json();}getSnapshot(){return this.snapshot}};function f(i){return new l(i)}
2
- export{f as createGradual,s as evaluateFlag};//# sourceMappingURL=index.js.map
1
+ function p(e){let t=0;for(let n=0;n<e.length;n++){let a=e.charCodeAt(n);t=(t<<5)-t+a,t|=0;}return Math.abs(t)}function l(e,t,n){let a=t[n.bucketContextKind]?.[n.bucketAttributeKey],o=n.seed??"",r=`${e}:${o}:${String(a??"anonymous")}`;return p(r)%1e5}function d(e,t,n){let a=0;for(let r of e.variations)if(a+=r.weight,t<a)return n[r.variationKey];let o=e.variations.at(-1);return o?n[o.variationKey]:void 0}function c(e,t){let{contextKind:n,attributeKey:a,operator:o,value:r}=e,i=t[n]?.[a];switch(o){case "equals":return i===r;case "not_equals":return i!==r;case "contains":return typeof i=="string"&&typeof r=="string"||Array.isArray(i)?i.includes(r):false;case "not_contains":return typeof i=="string"&&typeof r=="string"||Array.isArray(i)?!i.includes(r):true;case "starts_with":return typeof i=="string"&&typeof r=="string"?i.startsWith(r):false;case "ends_with":return typeof i=="string"&&typeof r=="string"?i.endsWith(r):false;case "greater_than":return typeof i=="number"&&typeof r=="number"?i>r:false;case "less_than":return typeof i=="number"&&typeof r=="number"?i<r:false;case "greater_than_or_equal":return typeof i=="number"&&typeof r=="number"?i>=r:false;case "less_than_or_equal":return typeof i=="number"&&typeof r=="number"?i<=r:false;case "in":return Array.isArray(r)?r.includes(i):false;case "not_in":return Array.isArray(r)?!r.includes(i):true;case "exists":return i!=null;case "not_exists":return i==null;default:return false}}function h(e,t){return e.every(n=>c(n,t))}function f(e,t){return h(e.conditions,t)}function v(e,t,n){switch(e.type){case "individual":return e.contextKind&&e.attributeKey&&e.attributeValue!==void 0?t[e.contextKind]?.[e.attributeKey]===e.attributeValue:false;case "rule":return e.conditions?h(e.conditions,t):false;case "segment":if(e.segmentKey){let a=n[e.segmentKey];if(a)return f(a,t)}return false;default:return false}}function y(e,t,n,a){if(e.rollout){let o=l(t,n,e.rollout);return d(e.rollout,o,a)}if(e.variationKey)return a[e.variationKey]}function m(e,t){if(e.defaultRollout){let n=l(e.key,t,e.defaultRollout);return d(e.defaultRollout,n,e.variations)}if(e.defaultVariationKey)return e.variations[e.defaultVariationKey]}function s(e,t,n){if(!e.enabled)return e.variations[e.offVariationKey]?.value;let a=[...e.targets].sort((r,i)=>r.sortOrder-i.sortOrder);for(let r of a)if(v(r,t,n)){let i=y(r,e.key,t,e.variations);if(i)return i.value}return m(e,t)?.value}var g="https://worker.gradual.so/api/v1",u=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};updateListeners=new Set;sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??g,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};let n=t.polling?.enabled??true,a=t.polling?.intervalMs??1e4;n&&this.initPromise.then(()=>{setInterval(async()=>{try{let o=this.snapshot?.version;if(await this.refresh(),this.snapshot&&this.snapshot.version!==o)for(let r of this.updateListeners)r();}catch(o){console.warn("Gradual: Polling refresh failed",o);}},a);});}async init(){let t=await fetch(`${this.baseUrl}/sdk/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:this.apiKey})});if(!t.ok){let o=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${o.error??t.statusText}`)}let n=await t.json();if(!n.valid)throw new Error(`Gradual: Invalid API key - ${n.error??"Unknown error"}`);let a=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!a.ok){let o=await a.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${o.error??a.statusText}`)}this.snapshot=await a.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){let n={},a=new Set([...Object.keys(this.identifiedContext),...Object.keys(t?.context??{})]);for(let o of a)n[o]={...this.identifiedContext[o],...t?.context?.[o]};return n}evaluate(t,n){let a=this.ensureReady();if(!a.flags)return;let o=a.flags[t];if(o)return s(o,n,a.segments??{})}async ready(){await this.initPromise;}isReady(){return this.snapshot!==null}async isEnabled(t,n){await this.initPromise;let a=this.evaluate(t,this.mergeContext(n));return typeof a=="boolean"?a:false}async get(t,n){await this.initPromise;let a=this.evaluate(t,this.mergeContext(n));return a??n.fallback}isEnabledSync(t,n){let a=this.evaluate(t,this.mergeContext(n));return typeof a=="boolean"?a:false}getSync(t,n){let a=this.evaluate(t,this.mergeContext(n));return a??n.fallback}identify(t){this.identifiedContext={...t};}reset(){this.identifiedContext={};}async refresh(){let t=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!t.ok){let n=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to refresh - ${n.error??t.statusText}`)}this.snapshot=await t.json();}getSnapshot(){return this.snapshot}onUpdate(t){return this.updateListeners.add(t),()=>this.updateListeners.delete(t)}};function b(e){return new u(e)}
2
+ export{b as createGradual,s as evaluateFlag};//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/evaluator.ts","../src/client.ts"],"names":["evaluateCondition","condition","context","contextKind","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","evaluateFlag","flag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","pollingEnabled","pollingIntervalMs","error","initResponse","initData","snapshotResponse","merged","allKinds","kind","key","snapshot","response","createGradual"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n EvaluationContext,\n SnapshotFlag,\n SnapshotRuleCondition,\n SnapshotSegment,\n SnapshotTarget,\n} from \"./types\";\n\nfunction evaluateCondition(\n condition: SnapshotRuleCondition,\n context: EvaluationContext\n): boolean {\n const { contextKind, attributeKey, operator, value } = condition;\n const contextValue = context[contextKind]?.[attributeKey];\n\n switch (operator) {\n case \"equals\":\n return contextValue === value;\n\n case \"not_equals\":\n return contextValue !== value;\n\n case \"contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return contextValue.includes(value);\n }\n return false;\n\n case \"not_contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return !contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return !contextValue.includes(value);\n }\n return true;\n\n case \"starts_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.startsWith(value);\n }\n return false;\n\n case \"ends_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.endsWith(value);\n }\n return false;\n\n case \"greater_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue > value;\n }\n return false;\n\n case \"less_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue < value;\n }\n return false;\n\n case \"greater_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue >= value;\n }\n return false;\n\n case \"less_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue <= value;\n }\n return false;\n\n case \"in\":\n if (Array.isArray(value)) {\n return value.includes(contextValue);\n }\n return false;\n\n case \"not_in\":\n if (Array.isArray(value)) {\n return !value.includes(contextValue);\n }\n return true;\n\n case \"exists\":\n return contextValue !== undefined && contextValue !== null;\n\n case \"not_exists\":\n return contextValue === undefined || contextValue === null;\n\n default:\n return false;\n }\n}\n\nfunction evaluateConditions(\n conditions: SnapshotRuleCondition[],\n context: EvaluationContext\n): boolean {\n return conditions.every((condition) => evaluateCondition(condition, context));\n}\n\nfunction evaluateSegment(\n segment: SnapshotSegment,\n context: EvaluationContext\n): boolean {\n return evaluateConditions(segment.conditions, context);\n}\n\nfunction evaluateTarget(\n target: SnapshotTarget,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): boolean {\n switch (target.type) {\n case \"individual\":\n if (\n target.contextKind &&\n target.attributeKey &&\n target.attributeValue !== undefined\n ) {\n return (\n context[target.contextKind]?.[target.attributeKey] ===\n target.attributeValue\n );\n }\n return false;\n\n case \"rule\":\n if (target.conditions) {\n return evaluateConditions(target.conditions, context);\n }\n return false;\n\n case \"segment\":\n if (target.segmentKey) {\n const segment = segments[target.segmentKey];\n if (segment) {\n return evaluateSegment(segment, context);\n }\n }\n return false;\n\n default:\n return false;\n }\n}\n\nexport function evaluateFlag(\n flag: SnapshotFlag,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): unknown {\n if (!flag.enabled) {\n const offVariation = flag.variations[flag.offVariationKey];\n return offVariation?.value;\n }\n\n const sortedTargets = [...flag.targets].sort(\n (a, b) => a.sortOrder - b.sortOrder\n );\n\n for (const target of sortedTargets) {\n if (evaluateTarget(target, context, segments)) {\n const variation = flag.variations[target.variationKey];\n if (variation) {\n return variation.value;\n }\n }\n }\n\n const defaultVariation = flag.variations[flag.defaultVariationKey];\n return defaultVariation?.value;\n}\n","import { evaluateFlag } from \"./evaluator\";\nimport type {\n EnvironmentSnapshot,\n EvaluationContext,\n FlagOptions,\n GradualOptions,\n IsEnabledOptions,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://worker.gradual.so/api/v1\";\n\nexport interface Gradual {\n /** Wait for the SDK to be ready (snapshot fetched) */\n ready(): Promise<void>;\n\n /** Check if the SDK is ready for sync access */\n isReady(): boolean;\n\n /** Check if a boolean flag is enabled */\n isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean>;\n\n /** Get a flag value with type inference from fallback */\n get<T>(key: string, options: FlagOptions<T>): Promise<T>;\n\n /** Set persistent user context for all evaluations */\n identify(context: EvaluationContext): void;\n\n /** Clear the identified user context */\n reset(): void;\n\n /** Refresh the snapshot from the server */\n refresh(): Promise<void>;\n\n /** Get the current snapshot (for debugging) */\n getSnapshot(): EnvironmentSnapshot | null;\n\n /** Sync methods (throw if not ready) */\n sync: GradualSync;\n}\n\nexport interface GradualSync {\n /** Sync version of isEnabled (throws if not ready) */\n isEnabled(key: string, options?: IsEnabledOptions): boolean;\n\n /** Sync version of get (throws if not ready) */\n get<T>(key: string, options: FlagOptions<T>): T;\n}\n\nclass GradualClient implements Gradual {\n private readonly apiKey: string;\n private readonly environment: string;\n private readonly baseUrl: string;\n private readonly initPromise: Promise<void>;\n private snapshot: EnvironmentSnapshot | null = null;\n private identifiedContext: EvaluationContext = {};\n\n readonly sync: GradualSync;\n\n constructor(options: GradualOptions) {\n this.apiKey = options.apiKey;\n this.environment = options.environment;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n this.initPromise = this.init();\n\n this.sync = {\n isEnabled: this.isEnabledSync.bind(this),\n get: this.getSync.bind(this),\n };\n\n // Polling defaults: enabled=true, interval=10000ms\n const pollingEnabled = options.polling?.enabled ?? true;\n const pollingIntervalMs = options.polling?.intervalMs ?? 10_000;\n\n // Start polling after init if enabled\n if (pollingEnabled) {\n this.initPromise.then(() => {\n setInterval(async () => {\n try {\n await this.refresh();\n } catch (error) {\n // Silent fail - don't crash the app\n console.warn(\"Gradual: Polling refresh failed\", error);\n }\n }, pollingIntervalMs);\n });\n }\n }\n\n private async init(): Promise<void> {\n const initResponse = await fetch(`${this.baseUrl}/sdk/init`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ apiKey: this.apiKey }),\n });\n\n if (!initResponse.ok) {\n const error = await initResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to initialize - ${(error as { error?: string }).error ?? initResponse.statusText}`\n );\n }\n\n const initData = (await initResponse.json()) as {\n valid: boolean;\n error?: string;\n };\n\n if (!initData.valid) {\n throw new Error(\n `Gradual: Invalid API key - ${initData.error ?? \"Unknown error\"}`\n );\n }\n\n const snapshotResponse = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!snapshotResponse.ok) {\n const error = await snapshotResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to fetch snapshot - ${(error as { error?: string }).error ?? snapshotResponse.statusText}`\n );\n }\n\n this.snapshot = (await snapshotResponse.json()) as EnvironmentSnapshot;\n }\n\n private ensureReady(): EnvironmentSnapshot {\n if (!this.snapshot) {\n throw new Error(\n \"Gradual: SDK not ready. Use await ready() or async methods.\"\n );\n }\n return this.snapshot;\n }\n\n private mergeContext(options?: {\n context?: EvaluationContext;\n }): EvaluationContext {\n const merged: EvaluationContext = {};\n const allKinds = new Set([\n ...Object.keys(this.identifiedContext),\n ...Object.keys(options?.context ?? {}),\n ]);\n for (const kind of allKinds) {\n merged[kind] = {\n ...this.identifiedContext[kind],\n ...options?.context?.[kind],\n };\n }\n return merged;\n }\n\n private evaluate(key: string, context: EvaluationContext): unknown {\n const snapshot = this.ensureReady();\n if (!snapshot.flags) {\n return undefined;\n }\n const flag = snapshot.flags[key];\n if (!flag) {\n return undefined;\n }\n return evaluateFlag(flag, context, snapshot.segments ?? {});\n }\n\n async ready(): Promise<void> {\n await this.initPromise;\n }\n\n isReady(): boolean {\n return this.snapshot !== null;\n }\n\n async isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n async get<T>(key: string, options: FlagOptions<T>): Promise<T> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n private isEnabledSync(key: string, options?: IsEnabledOptions): boolean {\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n private getSync<T>(key: string, options: FlagOptions<T>): T {\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n identify(context: EvaluationContext): void {\n this.identifiedContext = { ...context };\n }\n\n reset(): void {\n this.identifiedContext = {};\n }\n\n async refresh(): Promise<void> {\n const response = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to refresh - ${(error as { error?: string }).error ?? response.statusText}`\n );\n }\n\n this.snapshot = (await response.json()) as EnvironmentSnapshot;\n }\n\n getSnapshot(): EnvironmentSnapshot | null {\n return this.snapshot;\n }\n}\n\n/**\n * Create a Gradual feature flag client\n *\n * @example\n * ```ts\n * const gradual = createGradual({\n * apiKey: 'gra_xxx',\n * environment: 'production'\n * })\n *\n * // Boolean flags\n * const enabled = await gradual.isEnabled('new-feature')\n *\n * // Typed values (inferred from fallback)\n * const theme = await gradual.get('theme', { fallback: 'dark' })\n *\n * // With user context\n * gradual.identify({ userId: '123', plan: 'pro' })\n * const proFeature = await gradual.isEnabled('pro-feature')\n * ```\n */\nexport function createGradual(options: GradualOptions): Gradual {\n return new GradualClient(options);\n}\n"]}
1
+ {"version":3,"sources":["../src/evaluator.ts","../src/client.ts"],"names":["hashString","str","hash","i","char","getBucketValue","flagKey","context","rollout","bucketKey","seed","hashInput","selectVariationFromRollout","bucketValue","variations","cumulative","rv","lastVariation","evaluateCondition","condition","contextKind","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","resolveTargetVariation","resolveDefaultVariation","flag","evaluateFlag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","pollingEnabled","pollingIntervalMs","previousVersion","cb","error","initResponse","initData","snapshotResponse","merged","allKinds","kind","key","snapshot","response","callback","createGradual"],"mappings":"AAUA,SAASA,CAAAA,CAAWC,CAAAA,CAAqB,CACvC,IAAIC,CAAAA,CAAO,CAAA,CACX,IAAA,IAASC,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAIF,CAAAA,CAAI,MAAA,CAAQE,IAAK,CACnC,IAAMC,CAAAA,CAAOH,CAAAA,CAAI,UAAA,CAAWE,CAAC,CAAA,CAE7BD,CAAAA,CAAAA,CAAQA,CAAAA,EAAQ,CAAA,EAAKA,CAAAA,CAAOE,CAAAA,CAE5BF,CAAAA,EAAQ,EACV,CACA,OAAO,KAAK,GAAA,CAAIA,CAAI,CACtB,CAEA,SAASG,CAAAA,CACPC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACQ,CACR,IAAMC,CAAAA,CACJF,CAAAA,CAAQC,CAAAA,CAAQ,iBAAiB,CAAA,GAAIA,EAAQ,kBAAkB,CAAA,CAC3DE,CAAAA,CAAOF,CAAAA,CAAQ,IAAA,EAAQ,EAAA,CACvBG,CAAAA,CAAY,CAAA,EAAGL,CAAO,CAAA,CAAA,EAAII,CAAI,CAAA,CAAA,EAAI,MAAA,CAAOD,CAAAA,EAAa,WAAW,CAAC,GACxE,OAAOT,CAAAA,CAAWW,CAAS,CAAA,CAAI,GACjC,CAEA,SAASC,CAAAA,CACPJ,CAAAA,CACAK,CAAAA,CACAC,CAAAA,CAC+B,CAC/B,IAAIC,CAAAA,CAAa,CAAA,CAEjB,IAAA,IAAWC,KAAMR,CAAAA,CAAQ,UAAA,CAEvB,GADAO,CAAAA,EAAcC,CAAAA,CAAG,MAAA,CACbH,CAAAA,CAAcE,CAAAA,CAChB,OAAOD,CAAAA,CAAWE,CAAAA,CAAG,YAAY,CAAA,CAIrC,IAAMC,CAAAA,CAAgBT,CAAAA,CAAQ,WAAW,EAAA,CAAG,EAAE,CAAA,CAC9C,OAAOS,CAAAA,CAAgBH,CAAAA,CAAWG,CAAAA,CAAc,YAAY,CAAA,CAAI,MAClE,CAEA,SAASC,CAAAA,CACPC,CAAAA,CACAZ,CAAAA,CACS,CACT,GAAM,CAAE,WAAA,CAAAa,CAAAA,CAAa,YAAA,CAAAC,CAAAA,CAAc,QAAA,CAAAC,CAAAA,CAAU,KAAA,CAAAC,CAAM,CAAA,CAAIJ,CAAAA,CACjDK,CAAAA,CAAejB,CAAAA,CAAQa,CAAW,CAAA,GAAIC,CAAY,EAExD,OAAQC,CAAAA,EACN,KAAK,QAAA,CACH,OAAOE,CAAAA,GAAiBD,CAAAA,CAE1B,KAAK,YAAA,CACH,OAAOC,CAAAA,GAAiBD,CAAAA,CAE1B,KAAK,UAAA,CAIH,OAHI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,EAGrD,KAAA,CAAM,OAAA,CAAQC,CAAY,CAAA,CACrBA,CAAAA,CAAa,QAAA,CAASD,CAAK,CAAA,CAE7B,KAAA,CAET,KAAK,cAAA,CAIH,OAHI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,EAGrD,KAAA,CAAM,OAAA,CAAQC,CAAY,CAAA,CACrB,CAACA,CAAAA,CAAa,QAAA,CAASD,CAAK,CAAA,CAE9B,IAAA,CAET,KAAK,aAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,CAAa,UAAA,CAAWD,CAAK,CAAA,CAE/B,KAAA,CAET,KAAK,WAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,CAAa,QAAA,CAASD,CAAK,CAAA,CAE7B,KAAA,CAET,KAAK,cAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,CAAeD,CAAAA,CAEjB,KAAA,CAET,KAAK,WAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,CAAeD,CAAAA,CAEjB,MAET,KAAK,uBAAA,CACH,OAAI,OAAOC,CAAAA,EAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,EAAgBD,CAAAA,CAElB,KAAA,CAET,KAAK,oBAAA,CACH,OAAI,OAAOC,GAAiB,QAAA,EAAY,OAAOD,CAAAA,EAAU,QAAA,CAChDC,CAAAA,EAAgBD,CAAAA,CAElB,KAAA,CAET,KAAK,IAAA,CACH,OAAI,KAAA,CAAM,OAAA,CAAQA,CAAK,CAAA,CACdA,CAAAA,CAAM,QAAA,CAASC,CAAY,CAAA,CAE7B,KAAA,CAET,KAAK,QAAA,CACH,OAAI,KAAA,CAAM,OAAA,CAAQD,CAAK,CAAA,CACd,CAACA,CAAAA,CAAM,QAAA,CAASC,CAAY,CAAA,CAE9B,IAAA,CAET,KAAK,SACH,OAAqCA,CAAAA,EAAiB,IAAA,CAExD,KAAK,YAAA,CACH,OAAqCA,CAAAA,EAAiB,IAAA,CAExD,QACE,OAAO,MACX,CACF,CAEA,SAASC,CAAAA,CACPC,CAAAA,CACAnB,EACS,CACT,OAAOmB,CAAAA,CAAW,KAAA,CAAOP,CAAAA,EAAcD,CAAAA,CAAkBC,CAAAA,CAAWZ,CAAO,CAAC,CAC9E,CAEA,SAASoB,CAAAA,CACPC,CAAAA,CACArB,CAAAA,CACS,CACT,OAAOkB,CAAAA,CAAmBG,CAAAA,CAAQ,UAAA,CAAYrB,CAAO,CACvD,CAEA,SAASsB,CAAAA,CACPC,CAAAA,CACAvB,CAAAA,CACAwB,CAAAA,CACS,CACT,OAAQD,CAAAA,CAAO,IAAA,EACb,KAAK,YAAA,CACH,OACEA,CAAAA,CAAO,WAAA,EACPA,CAAAA,CAAO,YAAA,EACPA,CAAAA,CAAO,cAAA,GAAmB,MAAA,CAGxBvB,CAAAA,CAAQuB,CAAAA,CAAO,WAAW,CAAA,GAAIA,CAAAA,CAAO,YAAY,CAAA,GACjDA,EAAO,cAAA,CAGJ,KAAA,CAET,KAAK,MAAA,CACH,OAAIA,CAAAA,CAAO,UAAA,CACFL,CAAAA,CAAmBK,CAAAA,CAAO,UAAA,CAAYvB,CAAO,CAAA,CAE/C,KAAA,CAET,KAAK,SAAA,CACH,GAAIuB,EAAO,UAAA,CAAY,CACrB,IAAMF,CAAAA,CAAUG,CAAAA,CAASD,CAAAA,CAAO,UAAU,CAAA,CAC1C,GAAIF,CAAAA,CACF,OAAOD,CAAAA,CAAgBC,CAAAA,CAASrB,CAAO,CAE3C,CACA,OAAO,MAAA,CAET,QACE,OAAO,MACX,CACF,CAEA,SAASyB,CAAAA,CACPF,CAAAA,CACAxB,CAAAA,CACAC,CAAAA,CACAO,CAAAA,CAC+B,CAC/B,GAAIgB,CAAAA,CAAO,OAAA,CAAS,CAClB,IAAMjB,CAAAA,CAAcR,CAAAA,CAAeC,CAAAA,CAASC,CAAAA,CAASuB,CAAAA,CAAO,OAAO,CAAA,CACnE,OAAOlB,CAAAA,CAA2BkB,CAAAA,CAAO,OAAA,CAASjB,CAAAA,CAAaC,CAAU,CAC3E,CAEA,GAAIgB,CAAAA,CAAO,YAAA,CACT,OAAOhB,CAAAA,CAAWgB,CAAAA,CAAO,YAAY,CAIzC,CAEA,SAASG,CAAAA,CACPC,CAAAA,CACA3B,CAAAA,CAC+B,CAC/B,GAAI2B,CAAAA,CAAK,cAAA,CAAgB,CACvB,IAAMrB,CAAAA,CAAcR,CAAAA,CAAe6B,CAAAA,CAAK,GAAA,CAAK3B,CAAAA,CAAS2B,CAAAA,CAAK,cAAc,CAAA,CACzE,OAAOtB,CAAAA,CACLsB,CAAAA,CAAK,cAAA,CACLrB,CAAAA,CACAqB,CAAAA,CAAK,UACP,CACF,CAEA,GAAIA,CAAAA,CAAK,mBAAA,CACP,OAAOA,CAAAA,CAAK,UAAA,CAAWA,CAAAA,CAAK,mBAAmB,CAInD,CAEO,SAASC,CAAAA,CACdD,CAAAA,CACA3B,CAAAA,CACAwB,CAAAA,CACS,CACT,GAAI,CAACG,CAAAA,CAAK,OAAA,CAER,OADqBA,CAAAA,CAAK,UAAA,CAAWA,CAAAA,CAAK,eAAe,CAAA,EACpC,KAAA,CAGvB,IAAME,CAAAA,CAAgB,CAAC,GAAGF,CAAAA,CAAK,OAAO,CAAA,CAAE,IAAA,CACtC,CAACG,CAAAA,CAAGC,CAAAA,GAAMD,CAAAA,CAAE,SAAA,CAAYC,CAAAA,CAAE,SAC5B,CAAA,CAEA,IAAA,IAAWR,CAAAA,IAAUM,CAAAA,CACnB,GAAIP,CAAAA,CAAeC,CAAAA,CAAQvB,EAASwB,CAAQ,CAAA,CAAG,CAC7C,IAAMQ,CAAAA,CAAYP,CAAAA,CAChBF,CAAAA,CACAI,CAAAA,CAAK,GAAA,CACL3B,CAAAA,CACA2B,CAAAA,CAAK,UACP,CAAA,CACA,GAAIK,CAAAA,CACF,OAAOA,EAAU,KAErB,CAIF,OADyBN,CAAAA,CAAwBC,CAAAA,CAAM3B,CAAO,CAAA,EACrC,KAC3B,CC/PA,IAAMiC,CAAAA,CAAmB,kCAAA,CA0CnBC,CAAAA,CAAN,KAAuC,CACpB,MAAA,CACA,WAAA,CACA,QACA,WAAA,CACT,QAAA,CAAuC,IAAA,CACvC,iBAAA,CAAuC,EAAC,CAC/B,eAAA,CAAmC,IAAI,GAAA,CAE/C,IAAA,CAET,WAAA,CAAYC,CAAAA,CAAyB,CACnC,IAAA,CAAK,MAAA,CAASA,CAAAA,CAAQ,OACtB,IAAA,CAAK,WAAA,CAAcA,CAAAA,CAAQ,WAAA,CAC3B,IAAA,CAAK,OAAA,CAAUA,CAAAA,CAAQ,OAAA,EAAWF,CAAAA,CAClC,IAAA,CAAK,WAAA,CAAc,IAAA,CAAK,IAAA,EAAK,CAE7B,IAAA,CAAK,IAAA,CAAO,CACV,SAAA,CAAW,IAAA,CAAK,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA,CACvC,GAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAC7B,CAAA,CAEA,IAAMG,CAAAA,CAAiBD,CAAAA,CAAQ,SAAS,OAAA,EAAW,IAAA,CAC7CE,CAAAA,CAAoBF,CAAAA,CAAQ,OAAA,EAAS,UAAA,EAAc,GAAA,CAErDC,CAAAA,EACF,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAM,CAC1B,WAAA,CAAY,SAAY,CACtB,GAAI,CACF,IAAME,CAAAA,CAAkB,IAAA,CAAK,QAAA,EAAU,OAAA,CAEvC,GADA,MAAM,IAAA,CAAK,OAAA,EAAQ,CACf,IAAA,CAAK,QAAA,EAAY,IAAA,CAAK,QAAA,CAAS,OAAA,GAAYA,EAC7C,IAAA,IAAWC,CAAAA,IAAM,IAAA,CAAK,eAAA,CACpBA,CAAAA,GAGN,CAAA,MAASC,CAAAA,CAAO,CACd,OAAA,CAAQ,IAAA,CAAK,iCAAA,CAAmCA,CAAK,EACvD,CACF,CAAA,CAAGH,CAAiB,EACtB,CAAC,EAEL,CAEA,MAAc,IAAA,EAAsB,CAClC,IAAMI,CAAAA,CAAe,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,SAAA,CAAA,CAAa,CAC3D,OAAQ,MAAA,CACR,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAA,CAC9C,IAAA,CAAM,IAAA,CAAK,SAAA,CAAU,CAAE,MAAA,CAAQ,IAAA,CAAK,MAAO,CAAC,CAC9C,CAAC,EAED,GAAI,CAACA,CAAAA,CAAa,EAAA,CAAI,CACpB,IAAMD,CAAAA,CAAQ,MAAMC,CAAAA,CAAa,IAAA,EAAK,CAAE,KAAA,CAAM,KAAO,EAAC,CAAE,CAAA,CACxD,MAAM,IAAI,KAAA,CACR,CAAA,gCAAA,EAAoCD,CAAAA,CAA6B,KAAA,EAASC,CAAAA,CAAa,UAAU,CAAA,CACnG,CACF,CAEA,IAAMC,CAAAA,CAAY,MAAMD,CAAAA,CAAa,IAAA,EAAK,CAK1C,GAAI,CAACC,CAAAA,CAAS,KAAA,CACZ,MAAM,IAAI,KAAA,CACR,CAAA,2BAAA,EAA8BA,CAAAA,CAAS,KAAA,EAAS,eAAe,CAAA,CACjE,CAAA,CAGF,IAAMC,CAAAA,CAAmB,MAAM,KAAA,CAC7B,GAAG,IAAA,CAAK,OAAO,CAAA,0BAAA,EAA6B,kBAAA,CAAmB,IAAA,CAAK,WAAW,CAAC,CAAA,CAAA,CAChF,CAAE,OAAA,CAAS,CAAE,aAAA,CAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAG,CAAE,CACxD,CAAA,CAEA,GAAI,CAACA,CAAAA,CAAiB,EAAA,CAAI,CACxB,IAAMH,CAAAA,CAAQ,MAAMG,CAAAA,CAAiB,IAAA,EAAK,CAAE,KAAA,CAAM,KAAO,GAAG,CAAA,CAC5D,MAAM,IAAI,KAAA,CACR,CAAA,oCAAA,EAAwCH,CAAAA,CAA6B,KAAA,EAASG,CAAAA,CAAiB,UAAU,CAAA,CAC3G,CACF,CAEA,IAAA,CAAK,QAAA,CAAY,MAAMA,CAAAA,CAAiB,OAC1C,CAEQ,WAAA,EAAmC,CACzC,GAAI,CAAC,IAAA,CAAK,QAAA,CACR,MAAM,IAAI,KAAA,CACR,6DACF,CAAA,CAEF,OAAO,IAAA,CAAK,QACd,CAEQ,YAAA,CAAaR,CAAAA,CAEC,CACpB,IAAMS,CAAAA,CAA4B,EAAC,CAC7BC,CAAAA,CAAW,IAAI,GAAA,CAAI,CACvB,GAAG,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,iBAAiB,EACrC,GAAG,MAAA,CAAO,IAAA,CAAKV,CAAAA,EAAS,OAAA,EAAW,EAAE,CACvC,CAAC,CAAA,CACD,IAAA,IAAWW,CAAAA,IAAQD,CAAAA,CACjBD,CAAAA,CAAOE,CAAI,CAAA,CAAI,CACb,GAAG,IAAA,CAAK,iBAAA,CAAkBA,CAAI,CAAA,CAC9B,GAAGX,CAAAA,EAAS,OAAA,GAAUW,CAAI,CAC5B,CAAA,CAEF,OAAOF,CACT,CAEQ,QAAA,CAASG,CAAAA,CAAa/C,EAAqC,CACjE,IAAMgD,CAAAA,CAAW,IAAA,CAAK,WAAA,EAAY,CAClC,GAAI,CAACA,CAAAA,CAAS,KAAA,CACZ,OAEF,IAAMrB,CAAAA,CAAOqB,CAAAA,CAAS,KAAA,CAAMD,CAAG,EAC/B,GAAKpB,CAAAA,CAGL,OAAOC,CAAAA,CAAaD,CAAAA,CAAM3B,CAAAA,CAASgD,CAAAA,CAAS,QAAA,EAAY,EAAE,CAC5D,CAEA,MAAM,KAAA,EAAuB,CAC3B,MAAM,KAAK,YACb,CAEA,OAAA,EAAmB,CACjB,OAAO,IAAA,CAAK,QAAA,GAAa,IAC3B,CAEA,MAAM,SAAA,CAAUD,CAAAA,CAAaZ,CAAAA,CAA8C,CACzE,MAAM,IAAA,CAAK,YACX,IAAMnB,CAAAA,CAAQ,IAAA,CAAK,QAAA,CAAS+B,CAAAA,CAAK,IAAA,CAAK,YAAA,CAAaZ,CAAO,CAAC,CAAA,CAC3D,OAAO,OAAOnB,CAAAA,EAAU,SAAA,CAAYA,CAAAA,CAAQ,KAC9C,CAEA,MAAM,GAAA,CAAO+B,CAAAA,CAAaZ,CAAAA,CAAqC,CAC7D,MAAM,IAAA,CAAK,WAAA,CACX,IAAMnB,CAAAA,CAAQ,IAAA,CAAK,QAAA,CAAS+B,CAAAA,CAAK,IAAA,CAAK,YAAA,CAAaZ,CAAO,CAAC,CAAA,CAC3D,OAA8BnB,CAAAA,EAE1BmB,CAAAA,CAAQ,QACd,CAEQ,aAAA,CAAcY,CAAAA,CAAaZ,CAAAA,CAAqC,CACtE,IAAMnB,CAAAA,CAAQ,IAAA,CAAK,QAAA,CAAS+B,CAAAA,CAAK,IAAA,CAAK,aAAaZ,CAAO,CAAC,CAAA,CAC3D,OAAO,OAAOnB,CAAAA,EAAU,SAAA,CAAYA,CAAAA,CAAQ,KAC9C,CAEQ,OAAA,CAAW+B,CAAAA,CAAaZ,CAAAA,CAA4B,CAC1D,IAAMnB,CAAAA,CAAQ,KAAK,QAAA,CAAS+B,CAAAA,CAAK,IAAA,CAAK,YAAA,CAAaZ,CAAO,CAAC,CAAA,CAC3D,OAA8BnB,CAAAA,EAE1BmB,CAAAA,CAAQ,QACd,CAEA,QAAA,CAASnC,CAAAA,CAAkC,CACzC,IAAA,CAAK,kBAAoB,CAAE,GAAGA,CAAQ,EACxC,CAEA,KAAA,EAAc,CACZ,IAAA,CAAK,iBAAA,CAAoB,GAC3B,CAEA,MAAM,OAAA,EAAyB,CAC7B,IAAMiD,EAAW,MAAM,KAAA,CACrB,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,0BAAA,EAA6B,kBAAA,CAAmB,IAAA,CAAK,WAAW,CAAC,CAAA,CAAA,CAChF,CAAE,OAAA,CAAS,CAAE,aAAA,CAAe,CAAA,OAAA,EAAU,KAAK,MAAM,CAAA,CAAG,CAAE,CACxD,CAAA,CAEA,GAAI,CAACA,CAAAA,CAAS,EAAA,CAAI,CAChB,IAAMT,CAAAA,CAAQ,MAAMS,CAAAA,CAAS,IAAA,EAAK,CAAE,MAAM,KAAO,EAAC,CAAE,CAAA,CACpD,MAAM,IAAI,KAAA,CACR,CAAA,6BAAA,EAAiCT,CAAAA,CAA6B,KAAA,EAASS,CAAAA,CAAS,UAAU,CAAA,CAC5F,CACF,CAEA,IAAA,CAAK,SAAY,MAAMA,CAAAA,CAAS,IAAA,GAClC,CAEA,WAAA,EAA0C,CACxC,OAAO,IAAA,CAAK,QACd,CAEA,QAAA,CAASC,CAAAA,CAAkC,CACzC,OAAA,IAAA,CAAK,eAAA,CAAgB,IAAIA,CAAQ,CAAA,CAC1B,IAAM,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAOA,CAAQ,CACnD,CACF,CAAA,CAuBO,SAASC,CAAAA,CAAchB,CAAAA,CAAkC,CAC9D,OAAO,IAAID,CAAAA,CAAcC,CAAO,CAClC","file":"index.js","sourcesContent":["import type {\n EvaluationContext,\n SnapshotFlag,\n SnapshotRollout,\n SnapshotRuleCondition,\n SnapshotSegment,\n SnapshotTarget,\n SnapshotVariation,\n} from \"./types\";\n\nfunction hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n // biome-ignore lint/suspicious/noBitwiseOperators: intentional hash algorithm using bitwise shift\n hash = (hash << 5) - hash + char;\n // biome-ignore lint/suspicious/noBitwiseOperators: converting to 32-bit integer\n hash |= 0;\n }\n return Math.abs(hash);\n}\n\nfunction getBucketValue(\n flagKey: string,\n context: EvaluationContext,\n rollout: SnapshotRollout\n): number {\n const bucketKey =\n context[rollout.bucketContextKind]?.[rollout.bucketAttributeKey];\n const seed = rollout.seed ?? \"\";\n const hashInput = `${flagKey}:${seed}:${String(bucketKey ?? \"anonymous\")}`;\n return hashString(hashInput) % 100_000;\n}\n\nfunction selectVariationFromRollout(\n rollout: SnapshotRollout,\n bucketValue: number,\n variations: Record<string, SnapshotVariation>\n): SnapshotVariation | undefined {\n let cumulative = 0;\n\n for (const rv of rollout.variations) {\n cumulative += rv.weight;\n if (bucketValue < cumulative) {\n return variations[rv.variationKey];\n }\n }\n\n const lastVariation = rollout.variations.at(-1);\n return lastVariation ? variations[lastVariation.variationKey] : undefined;\n}\n\nfunction evaluateCondition(\n condition: SnapshotRuleCondition,\n context: EvaluationContext\n): boolean {\n const { contextKind, attributeKey, operator, value } = condition;\n const contextValue = context[contextKind]?.[attributeKey];\n\n switch (operator) {\n case \"equals\":\n return contextValue === value;\n\n case \"not_equals\":\n return contextValue !== value;\n\n case \"contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return contextValue.includes(value);\n }\n return false;\n\n case \"not_contains\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return !contextValue.includes(value);\n }\n if (Array.isArray(contextValue)) {\n return !contextValue.includes(value);\n }\n return true;\n\n case \"starts_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.startsWith(value);\n }\n return false;\n\n case \"ends_with\":\n if (typeof contextValue === \"string\" && typeof value === \"string\") {\n return contextValue.endsWith(value);\n }\n return false;\n\n case \"greater_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue > value;\n }\n return false;\n\n case \"less_than\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue < value;\n }\n return false;\n\n case \"greater_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue >= value;\n }\n return false;\n\n case \"less_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof value === \"number\") {\n return contextValue <= value;\n }\n return false;\n\n case \"in\":\n if (Array.isArray(value)) {\n return value.includes(contextValue);\n }\n return false;\n\n case \"not_in\":\n if (Array.isArray(value)) {\n return !value.includes(contextValue);\n }\n return true;\n\n case \"exists\":\n return contextValue !== undefined && contextValue !== null;\n\n case \"not_exists\":\n return contextValue === undefined || contextValue === null;\n\n default:\n return false;\n }\n}\n\nfunction evaluateConditions(\n conditions: SnapshotRuleCondition[],\n context: EvaluationContext\n): boolean {\n return conditions.every((condition) => evaluateCondition(condition, context));\n}\n\nfunction evaluateSegment(\n segment: SnapshotSegment,\n context: EvaluationContext\n): boolean {\n return evaluateConditions(segment.conditions, context);\n}\n\nfunction evaluateTarget(\n target: SnapshotTarget,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): boolean {\n switch (target.type) {\n case \"individual\":\n if (\n target.contextKind &&\n target.attributeKey &&\n target.attributeValue !== undefined\n ) {\n return (\n context[target.contextKind]?.[target.attributeKey] ===\n target.attributeValue\n );\n }\n return false;\n\n case \"rule\":\n if (target.conditions) {\n return evaluateConditions(target.conditions, context);\n }\n return false;\n\n case \"segment\":\n if (target.segmentKey) {\n const segment = segments[target.segmentKey];\n if (segment) {\n return evaluateSegment(segment, context);\n }\n }\n return false;\n\n default:\n return false;\n }\n}\n\nfunction resolveTargetVariation(\n target: SnapshotTarget,\n flagKey: string,\n context: EvaluationContext,\n variations: Record<string, SnapshotVariation>\n): SnapshotVariation | undefined {\n if (target.rollout) {\n const bucketValue = getBucketValue(flagKey, context, target.rollout);\n return selectVariationFromRollout(target.rollout, bucketValue, variations);\n }\n\n if (target.variationKey) {\n return variations[target.variationKey];\n }\n\n return undefined;\n}\n\nfunction resolveDefaultVariation(\n flag: SnapshotFlag,\n context: EvaluationContext\n): SnapshotVariation | undefined {\n if (flag.defaultRollout) {\n const bucketValue = getBucketValue(flag.key, context, flag.defaultRollout);\n return selectVariationFromRollout(\n flag.defaultRollout,\n bucketValue,\n flag.variations\n );\n }\n\n if (flag.defaultVariationKey) {\n return flag.variations[flag.defaultVariationKey];\n }\n\n return undefined;\n}\n\nexport function evaluateFlag(\n flag: SnapshotFlag,\n context: EvaluationContext,\n segments: Record<string, SnapshotSegment>\n): unknown {\n if (!flag.enabled) {\n const offVariation = flag.variations[flag.offVariationKey];\n return offVariation?.value;\n }\n\n const sortedTargets = [...flag.targets].sort(\n (a, b) => a.sortOrder - b.sortOrder\n );\n\n for (const target of sortedTargets) {\n if (evaluateTarget(target, context, segments)) {\n const variation = resolveTargetVariation(\n target,\n flag.key,\n context,\n flag.variations\n );\n if (variation) {\n return variation.value;\n }\n }\n }\n\n const defaultVariation = resolveDefaultVariation(flag, context);\n return defaultVariation?.value;\n}\n","import { evaluateFlag } from \"./evaluator\";\nimport type {\n EnvironmentSnapshot,\n EvaluationContext,\n FlagOptions,\n GradualOptions,\n IsEnabledOptions,\n} from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://worker.gradual.so/api/v1\";\n\nexport interface Gradual {\n /** Wait for the SDK to be ready (snapshot fetched) */\n ready(): Promise<void>;\n\n /** Check if the SDK is ready for sync access */\n isReady(): boolean;\n\n /** Check if a boolean flag is enabled */\n isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean>;\n\n /** Get a flag value with type inference from fallback */\n get<T>(key: string, options: FlagOptions<T>): Promise<T>;\n\n /** Set persistent user context for all evaluations */\n identify(context: EvaluationContext): void;\n\n /** Clear the identified user context */\n reset(): void;\n\n /** Refresh the snapshot from the server */\n refresh(): Promise<void>;\n\n /** Get the current snapshot (for debugging) */\n getSnapshot(): EnvironmentSnapshot | null;\n\n /** Subscribe to snapshot updates from polling (returns unsubscribe function) */\n onUpdate(callback: () => void): () => void;\n\n /** Sync methods (throw if not ready) */\n sync: GradualSync;\n}\n\nexport interface GradualSync {\n /** Sync version of isEnabled (throws if not ready) */\n isEnabled(key: string, options?: IsEnabledOptions): boolean;\n\n /** Sync version of get (throws if not ready) */\n get<T>(key: string, options: FlagOptions<T>): T;\n}\n\nclass GradualClient implements Gradual {\n private readonly apiKey: string;\n private readonly environment: string;\n private readonly baseUrl: string;\n private readonly initPromise: Promise<void>;\n private snapshot: EnvironmentSnapshot | null = null;\n private identifiedContext: EvaluationContext = {};\n private readonly updateListeners: Set<() => void> = new Set();\n\n readonly sync: GradualSync;\n\n constructor(options: GradualOptions) {\n this.apiKey = options.apiKey;\n this.environment = options.environment;\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n this.initPromise = this.init();\n\n this.sync = {\n isEnabled: this.isEnabledSync.bind(this),\n get: this.getSync.bind(this),\n };\n\n const pollingEnabled = options.polling?.enabled ?? true;\n const pollingIntervalMs = options.polling?.intervalMs ?? 10_000;\n\n if (pollingEnabled) {\n this.initPromise.then(() => {\n setInterval(async () => {\n try {\n const previousVersion = this.snapshot?.version;\n await this.refresh();\n if (this.snapshot && this.snapshot.version !== previousVersion) {\n for (const cb of this.updateListeners) {\n cb();\n }\n }\n } catch (error) {\n console.warn(\"Gradual: Polling refresh failed\", error);\n }\n }, pollingIntervalMs);\n });\n }\n }\n\n private async init(): Promise<void> {\n const initResponse = await fetch(`${this.baseUrl}/sdk/init`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ apiKey: this.apiKey }),\n });\n\n if (!initResponse.ok) {\n const error = await initResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to initialize - ${(error as { error?: string }).error ?? initResponse.statusText}`\n );\n }\n\n const initData = (await initResponse.json()) as {\n valid: boolean;\n error?: string;\n };\n\n if (!initData.valid) {\n throw new Error(\n `Gradual: Invalid API key - ${initData.error ?? \"Unknown error\"}`\n );\n }\n\n const snapshotResponse = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!snapshotResponse.ok) {\n const error = await snapshotResponse.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to fetch snapshot - ${(error as { error?: string }).error ?? snapshotResponse.statusText}`\n );\n }\n\n this.snapshot = (await snapshotResponse.json()) as EnvironmentSnapshot;\n }\n\n private ensureReady(): EnvironmentSnapshot {\n if (!this.snapshot) {\n throw new Error(\n \"Gradual: SDK not ready. Use await ready() or async methods.\"\n );\n }\n return this.snapshot;\n }\n\n private mergeContext(options?: {\n context?: EvaluationContext;\n }): EvaluationContext {\n const merged: EvaluationContext = {};\n const allKinds = new Set([\n ...Object.keys(this.identifiedContext),\n ...Object.keys(options?.context ?? {}),\n ]);\n for (const kind of allKinds) {\n merged[kind] = {\n ...this.identifiedContext[kind],\n ...options?.context?.[kind],\n };\n }\n return merged;\n }\n\n private evaluate(key: string, context: EvaluationContext): unknown {\n const snapshot = this.ensureReady();\n if (!snapshot.flags) {\n return undefined;\n }\n const flag = snapshot.flags[key];\n if (!flag) {\n return undefined;\n }\n return evaluateFlag(flag, context, snapshot.segments ?? {});\n }\n\n async ready(): Promise<void> {\n await this.initPromise;\n }\n\n isReady(): boolean {\n return this.snapshot !== null;\n }\n\n async isEnabled(key: string, options?: IsEnabledOptions): Promise<boolean> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n async get<T>(key: string, options: FlagOptions<T>): Promise<T> {\n await this.initPromise;\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n private isEnabledSync(key: string, options?: IsEnabledOptions): boolean {\n const value = this.evaluate(key, this.mergeContext(options));\n return typeof value === \"boolean\" ? value : false;\n }\n\n private getSync<T>(key: string, options: FlagOptions<T>): T {\n const value = this.evaluate(key, this.mergeContext(options));\n return value !== undefined && value !== null\n ? (value as T)\n : options.fallback;\n }\n\n identify(context: EvaluationContext): void {\n this.identifiedContext = { ...context };\n }\n\n reset(): void {\n this.identifiedContext = {};\n }\n\n async refresh(): Promise<void> {\n const response = await fetch(\n `${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,\n { headers: { Authorization: `Bearer ${this.apiKey}` } }\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n `Gradual: Failed to refresh - ${(error as { error?: string }).error ?? response.statusText}`\n );\n }\n\n this.snapshot = (await response.json()) as EnvironmentSnapshot;\n }\n\n getSnapshot(): EnvironmentSnapshot | null {\n return this.snapshot;\n }\n\n onUpdate(callback: () => void): () => void {\n this.updateListeners.add(callback);\n return () => this.updateListeners.delete(callback);\n }\n}\n\n/**\n * Create a Gradual feature flag client\n *\n * @example\n * ```ts\n * const gradual = createGradual({\n * apiKey: 'gra_xxx',\n * environment: 'production'\n * })\n *\n * // Boolean flags\n * const enabled = await gradual.isEnabled('new-feature')\n *\n * // Typed values (inferred from fallback)\n * const theme = await gradual.get('theme', { fallback: 'dark' })\n *\n * // With user context\n * gradual.identify({ userId: '123', plan: 'pro' })\n * const proFeature = await gradual.isEnabled('pro-feature')\n * ```\n */\nexport function createGradual(options: GradualOptions): Gradual {\n return new GradualClient(options);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradual-so/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Gradual feature flag SDK for TypeScript and JavaScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",