@stacksjs/feature-flags 0.74.34 → 0.74.35
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.js +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
class m extends Error{name="FeatureFlagError"}class y extends m{name="FeatureNotDefinedError";constructor(e){super(`Feature flag '${e}' has not been defined.`)}}class c extends m{name="InvalidFeatureValueError";constructor(e){super(`Feature values must be JSON-safe. ${e}`)}}class u extends m{name="InvalidFeatureScopeError"}class l extends m{name="FeatureFlagStoreError";constructor(e,t){super(e,t)}}function o(e){if(e===void 0)throw new c("Undefined is not a valid feature value.");if(typeof e==="number"&&!Number.isFinite(e))throw new c("NaN and Infinity are not valid feature values.");if(typeof e==="bigint"||typeof e==="function"||typeof e==="symbol")throw new c(`Values of type '${typeof e}' are not supported.`);try{let t=JSON.stringify(e);if(t===void 0)throw new c("The value cannot be serialized.");return JSON.parse(t)}catch(t){if(t instanceof c)throw t;throw new c(t instanceof Error?t.message:String(t))}}function w(e="feature_flags"){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new l(`Invalid feature flag table name '${e}'.`);return e}function I(e={}){let t=w(e.table),r=e.dialect??"sqlite";if(r==="mysql")return[`CREATE TABLE IF NOT EXISTS ${t} (name VARCHAR(191) NOT NULL,scope VARCHAR(512) NOT NULL,value TEXT NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,UNIQUE KEY ${t}_name_scope_unique (name, scope),KEY ${t}_scope_index (scope))`];let i=r==="postgres"?"TIMESTAMPTZ":"DATETIME";return[`CREATE TABLE IF NOT EXISTS ${t} (name VARCHAR(191) NOT NULL,scope VARCHAR(512) NOT NULL,value TEXT NOT NULL,created_at ${i} DEFAULT CURRENT_TIMESTAMP,updated_at ${i} DEFAULT CURRENT_TIMESTAMP,UNIQUE (name, scope))`,`CREATE INDEX IF NOT EXISTS ${t}_scope_index ON ${t}(scope)`]}async function O(e,t){if(!e.unsafe)throw new l("The database client does not support schema provisioning via unsafe SQL.");let r=e.unsafe(t);if(r&&typeof r.execute==="function")await r.execute();else await r}async function S(e,t={}){for(let r of I(t))await O(e,r)}function M(e){return JSON.stringify(o(e))}function P(e){try{return o(JSON.parse(e.value))}catch(t){throw new l(`Stored value for feature '${e.name}' is invalid JSON.`,{cause:t})}}function _(e){let t=e,r=t?.message??"";return t?.code==="23505"||t?.code==="ER_DUP_ENTRY"||/unique constraint|duplicate entry/i.test(r)}class F{database;table;ready;constructor(e,t={}){this.database=e;this.table=w(t.table),this.ready=t.autoCreate?S(e,{table:this.table,dialect:t.dialect}):Promise.resolve()}async get(e,t){await this.ready;let r=await this.database.selectFrom(this.table).where("name","=",e).where("scope","=",t).select(["name","value"]).executeTakeFirst();return r?P(r):void 0}async set(e,t,r){await this.ready;let i=M(r),a=new Date().toISOString();if(await this.database.selectFrom(this.table).where("name","=",e).where("scope","=",t).select(["name"]).executeTakeFirst()){await this.update(e,t,i,a);return}try{await this.database.insertInto(this.table).values({name:e,scope:t,value:i,created_at:a,updated_at:a}).execute()}catch(s){if(!_(s))throw s;await this.update(e,t,i,a)}}async update(e,t,r,i){await this.database.updateTable(this.table).set({value:r,updated_at:i}).where("name","=",e).where("scope","=",t).execute()}async delete(e,t){await this.ready,await this.database.deleteFrom(this.table).where("name","=",e).where("scope","=",t).execute()}async deleteForAllScopes(e){if(await this.ready,!e){await this.clear();return}for(let t of new Set(e))await this.database.deleteFrom(this.table).where("name","=",t).execute()}async clear(){await this.ready,await this.database.deleteFrom(this.table).execute()}async stored(e){await this.ready;let t=await this.database.selectFrom(this.table).where("scope","=",e).select(["name","value"]).execute(),r={};for(let i of t)Object.defineProperty(r,i.name,{value:P(i),enumerable:!0,configurable:!0,writable:!0});return r}}function Z(e,t){return new F(e,t)}class g{values=new Map;cloneValues;constructor(e={}){this.cloneValues=e.cloneValues??!0}clone(e){return this.cloneValues?o(e):e}async get(e,t){let r=this.values.get(t)?.get(e);return r===void 0?void 0:this.clone(r)}async set(e,t,r){let i=this.values.get(t);if(!i)i=new Map,this.values.set(t,i);i.set(e,this.clone(r))}async delete(e,t){let r=this.values.get(t);if(r?.delete(e),r?.size===0)this.values.delete(t)}async deleteForAllScopes(e){if(!e){this.values.clear();return}let t=new Set(e);for(let[r,i]of this.values){for(let a of t)i.delete(a);if(i.size===0)this.values.delete(r)}}async clear(){this.values.clear()}async stored(e){let t={};for(let[r,i]of this.values.get(e)??[])Object.defineProperty(t,r,{value:this.clone(i),enumerable:!0,configurable:!0,writable:!0});return t}}function ee(e){return new g(e)}import{createHash as k}from"crypto";function v(e){if(new TextEncoder().encode(e).byteLength<=512)return e;return`sha256:${k("sha256").update(e).digest("hex")}`}function b(e,t){if(e===null||typeof e!=="object"){if(typeof e==="bigint")return{$bigint:e.toString()};if(typeof e==="symbol"||typeof e==="function"||e===void 0)throw new u(`Feature scope contains unsupported value '${typeof e}'.`);if(typeof e==="number"&&!Number.isFinite(e))throw new u("Feature scope cannot contain NaN or Infinity.");return e}if(t.has(e))throw new u("Feature scope cannot contain circular references.");t.add(e);try{if(e instanceof Date)return{$date:e.toISOString()};if(Array.isArray(e))return e.map((a)=>b(a,t));let r=Object.getPrototypeOf(e);if(r!==Object.prototype&&r!==null)throw new u("Feature scope objects must be plain objects or expose featureFlagScope.");let i={};for(let a of Object.keys(e).sort())i[a]=b(e[a],t);return i}finally{t.delete(e)}}function L(e){return typeof e.featureFlagScope==="function"?e.featureFlagScope():e.featureFlagScope}function U(e){return(typeof e.featureFlagType==="function"?e.featureFlagType():e.featureFlagType)?.trim()||e.constructor?.name||"object"}function C(e,t){let r=typeof e.featureFlagType==="function"?e.featureFlagType():e.featureFlagType;if(typeof r==="string"&&r.trim())return r.trim();let i=e._definition;if(i&&typeof i==="object"){let a=i.name;if(typeof a==="string"&&a.trim())return a.trim()}return t.constructor?.name||"object"}function T(e){if(e===null||e===void 0)return"global";if(typeof e==="string")return v(`string:${e}`);if(typeof e==="number"){if(!Number.isFinite(e))throw new u("Feature scope cannot be NaN or Infinity.");return`number:${e}`}if(typeof e==="bigint")return`bigint:${e}`;if(typeof e==="boolean")return`boolean:${e}`;if(typeof e==="symbol"||typeof e==="function")throw new u(`Feature scope of type '${typeof e}' is not supported.`);let t=e;if("featureFlagScope"in t){let r=t,i=L(r);if(typeof i!=="string"&&typeof i!=="number"||String(i).length===0)throw new u("featureFlagScope must resolve to a non-empty string or number.");return v(`model:${U(r)}:${String(i)}`)}if("id"in t&&(typeof t.id==="string"||typeof t.id==="number")){let r=C(t,e);return v(`model:${r}:${String(t.id)}`)}return v(`object:${JSON.stringify(b(e,new WeakSet))}`)}function f(e){if(typeof e!=="string"||e.trim().length===0)throw TypeError("Feature flag names must be non-empty strings.");if(e!==e.trim())throw TypeError("Feature flag names cannot start or end with whitespace.");if(new TextEncoder().encode(e).byteLength>191)throw TypeError("Feature flag names cannot exceed 191 UTF-8 bytes.");return e}function E(e,t){return JSON.stringify([e,t])}function D(e){return typeof e==="function"?e:o(e)}function x(e,t,r){Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0})}class V{manager;scope;constructor(e,t){this.manager=e;this.scope=t}value(e){return this.manager.value(e,this.scope)}values(e){return this.manager.values(e,this.scope)}all(){return this.manager.all(this.scope)}active(e){return this.manager.active(e,this.scope)}inactive(e){return this.manager.inactive(e,this.scope)}activate(e,t=!0){return this.manager.activate(e,t,this.scope)}deactivate(e){return this.manager.deactivate(e,this.scope)}forget(e){return this.manager.forget(e,this.scope)}when(e,t,r){return this.manager.when(e,t,r,this.scope)}unless(e,t,r){return this.manager.unless(e,t,r,this.scope)}}class h{definitions=new Map;pending=new Map;listeners=new Set;resolveScope;driverSource;driverPromise;missingBehavior;constructor(e={}){this.driverSource=e.driver??new g,this.missingBehavior=e.missing??"false",this.resolveScope=e.scopeResolver??T}use(e){return this.driverSource=e,this.driverPromise=void 0,this.pending.clear(),this}missing(e){return this.missingBehavior=e,this}define(e,t){if(typeof e==="string"){if(t===void 0)throw TypeError(`A definition is required for feature '${e}'.`);return this.definitions.set(f(e),D(t)),this}for(let[r,i]of Object.entries(e))this.definitions.set(f(r),D(i));return this}defined(e){return this.definitions.has(f(e))}definedNames(){return[...this.definitions.keys()]}clearDefinitions(){return this.definitions.clear(),this}for(e){return new V(this,e)}async value(e,t=null){let r=f(e),i=this.resolveScope(t),a=E(r,i),n=this.pending.get(a);if(n)return n;let s=this.evaluate(r,t,i);this.pending.set(a,s);try{return await s}finally{if(this.pending.get(a)===s)this.pending.delete(a)}}async evaluate(e,t,r){let i=await this.driver(),a=await i.get(e,r);if(a!==void 0){let d=o(a);return await this.notify({name:e,scope:t,scopeKey:r,value:d,source:"stored"}),d}let n=this.definitions.get(e);if(n===void 0){if(this.missingBehavior==="throw")throw new y(e);let d=!1;return await this.notify({name:e,scope:t,scopeKey:r,value:d,source:"missing"}),d}let s=typeof n==="function"?await n(t,{name:e,scope:t,scopeKey:r}):n,p=o(s);return await i.set(e,r,p),await this.notify({name:e,scope:t,scopeKey:r,value:p,source:"resolver"}),p}async values(e,t=null){let r=[...new Set(e.map(f))],i=await Promise.all(r.map(async(a)=>[a,await this.value(a,t)]));return Object.fromEntries(i)}async all(e=null){let t=this.resolveScope(e),r=await(await this.driver()).stored(t),i={};for(let[a,n]of Object.entries(r))x(i,a,o(n));for(let a of this.definitions.keys())if(!Object.hasOwn(i,a))x(i,a,await this.value(a,e));return i}async active(e,t=null){return Boolean(await this.value(e,t))}async inactive(e,t=null){return!await this.active(e,t)}async activate(e,t=!0,r=null){let i=f(e),a=this.resolveScope(r);await this.awaitPending(i,a),await(await this.driver()).set(i,a,o(t))}deactivate(e,t=null){return this.activate(e,!1,t)}async forget(e,t=null){let r=f(e),i=this.resolveScope(t);await this.awaitPending(r,i),await(await this.driver()).delete(r,i)}async purge(e){let t=e===void 0?void 0:(Array.isArray(e)?e:[e]).map(f);await Promise.allSettled(this.pending.values()),await(await this.driver()).deleteForAllScopes(t)}async flush(){await Promise.allSettled(this.pending.values()),await(await this.driver()).clear()}async when(e,t,r,i=null){let a=await this.value(e,i);return Boolean(a)?t(a):r?.(a)}async unless(e,t,r,i=null){let a=await this.value(e,i);return Boolean(a)?r?.(a):t(a)}onEvaluated(e){return this.listeners.add(e),()=>this.listeners.delete(e)}async notify(e){await Promise.allSettled([...this.listeners].map((t)=>Promise.resolve().then(()=>t(e))))}async awaitPending(e,t){let r=this.pending.get(E(e,t));if(r)try{await r}catch{}}driver(){if(!this.driverPromise){let e=Promise.resolve(typeof this.driverSource==="function"?this.driverSource():this.driverSource);this.driverPromise=e,e.catch(()=>{if(this.driverPromise===e)this.driverPromise=void 0})}return this.driverPromise}}function ce(e){return new h(e)}function j(e){if(!Number.isFinite(e)||e<0||e>100)throw RangeError(`Percentage must be between 0 and 100. Received ${e}.`)}function N(e){let t=new TextEncoder().encode(e),r=2166136261;for(let i of t)r^=i,r=Math.imul(r,16777619);return(r>>>0)/4294967296*100}function fe(e){return j(e),(t,r)=>N(`${r.name}\x00${r.scopeKey}`)<e}function de(e){let t=Object.entries(e),r=t[t.length-1];if(!r)throw RangeError("At least one feature variant is required.");let i=0;for(let[a,n]of t){if(!Number.isFinite(n)||n<=0)throw RangeError(`Variant '${a}' must have a positive, finite weight.`);i+=n}return(a,n)=>{let s=N(`${n.name}\x00${n.scopeKey}`)/100*i,p=0;for(let[d,A]of t)if(p+=A,s<p)return d;return r[0]}}async function q(e,t){if(e.default==="database"){if(t!=="sqlite"&&t!=="mysql"&&t!=="singlestore"&&t!=="postgres")throw new l(`The feature flag database driver requires a SQL database. Received '${t??"undefined"}'.`);let{db:r}=await import("@stacksjs/database");return new F(r,{...e.drivers?.database,dialect:t==="mysql"||t==="singlestore"?"mysql":t==="postgres"?"postgres":"sqlite"})}return new g(e.drivers?.memory)}var R=new h({driver:async()=>{let{awaitConfig:e}=await import("@stacksjs/config"),t=await e();return R.missing(t.featureFlags.missing??"false"),q(t.featureFlags,t.database.default)}}),he=R;export{F as DatabaseFeatureFlagDriver,R as Feature,m as FeatureFlagError,h as FeatureFlagManager,l as FeatureFlagStoreError,y as FeatureNotDefinedError,u as InvalidFeatureScopeError,c as InvalidFeatureValueError,g as MemoryFeatureFlagDriver,V as ScopedFeatureFlags,Z as createDatabaseFeatureFlagDriver,ce as createFeatureFlags,ee as createMemoryFeatureFlagDriver,S as ensureFeatureFlagTable,N as featureBucket,I as featureFlagMigrationSql,w as featureFlagTableName,he as featureFlags,T as featureScopeKey,o as normalizeFeatureValue,fe as percentage,de as variants};
|
|
2
|
+
var S=import.meta.require;class m extends Error{name="FeatureFlagError"}class y extends m{name="FeatureNotDefinedError";constructor(e){super(`Feature flag '${e}' has not been defined.`)}}class c extends m{name="InvalidFeatureValueError";constructor(e){super(`Feature values must be JSON-safe. ${e}`)}}class u extends m{name="InvalidFeatureScopeError"}class l extends m{name="FeatureFlagStoreError";constructor(e,t){super(e,t)}}function o(e){if(e===void 0)throw new c("Undefined is not a valid feature value.");if(typeof e==="number"&&!Number.isFinite(e))throw new c("NaN and Infinity are not valid feature values.");if(typeof e==="bigint"||typeof e==="function"||typeof e==="symbol")throw new c(`Values of type '${typeof e}' are not supported.`);try{let t=JSON.stringify(e);if(t===void 0)throw new c("The value cannot be serialized.");return JSON.parse(t)}catch(t){if(t instanceof c)throw t;throw new c(t instanceof Error?t.message:String(t))}}function w(e="feature_flags"){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new l(`Invalid feature flag table name '${e}'.`);return e}function O(e={}){let t=w(e.table),r=e.dialect??"sqlite";if(r==="mysql")return[`CREATE TABLE IF NOT EXISTS ${t} (name VARCHAR(191) NOT NULL,scope VARCHAR(512) NOT NULL,value TEXT NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,UNIQUE KEY ${t}_name_scope_unique (name, scope),KEY ${t}_scope_index (scope))`];let i=r==="postgres"?"TIMESTAMPTZ":"DATETIME";return[`CREATE TABLE IF NOT EXISTS ${t} (name VARCHAR(191) NOT NULL,scope VARCHAR(512) NOT NULL,value TEXT NOT NULL,created_at ${i} DEFAULT CURRENT_TIMESTAMP,updated_at ${i} DEFAULT CURRENT_TIMESTAMP,UNIQUE (name, scope))`,`CREATE INDEX IF NOT EXISTS ${t}_scope_index ON ${t}(scope)`]}async function M(e,t){if(!e.unsafe)throw new l("The database client does not support schema provisioning via unsafe SQL.");let r=e.unsafe(t);if(r&&typeof r.execute==="function")await r.execute();else await r}async function P(e,t={}){for(let r of O(t))await M(e,r)}function _(e){return JSON.stringify(o(e))}function T(e){try{return o(JSON.parse(e.value))}catch(t){throw new l(`Stored value for feature '${e.name}' is invalid JSON.`,{cause:t})}}function k(e){let t=e,r=t?.message??"";return t?.code==="23505"||t?.code==="ER_DUP_ENTRY"||/unique constraint|duplicate entry/i.test(r)}class F{database;table;ready;constructor(e,t={}){this.database=e;this.table=w(t.table),this.ready=t.autoCreate?P(e,{table:this.table,dialect:t.dialect}):Promise.resolve()}async get(e,t){await this.ready;let r=await this.database.selectFrom(this.table).where("name","=",e).where("scope","=",t).select(["name","value"]).executeTakeFirst();return r?T(r):void 0}async set(e,t,r){await this.ready;let i=_(r),a=new Date().toISOString();if(await this.database.selectFrom(this.table).where("name","=",e).where("scope","=",t).select(["name"]).executeTakeFirst()){await this.update(e,t,i,a);return}try{await this.database.insertInto(this.table).values({name:e,scope:t,value:i,created_at:a,updated_at:a}).execute()}catch(s){if(!k(s))throw s;await this.update(e,t,i,a)}}async update(e,t,r,i){await this.database.updateTable(this.table).set({value:r,updated_at:i}).where("name","=",e).where("scope","=",t).execute()}async delete(e,t){await this.ready,await this.database.deleteFrom(this.table).where("name","=",e).where("scope","=",t).execute()}async deleteForAllScopes(e){if(await this.ready,!e){await this.clear();return}for(let t of new Set(e))await this.database.deleteFrom(this.table).where("name","=",t).execute()}async clear(){await this.ready,await this.database.deleteFrom(this.table).execute()}async stored(e){await this.ready;let t=await this.database.selectFrom(this.table).where("scope","=",e).select(["name","value"]).execute(),r={};for(let i of t)Object.defineProperty(r,i.name,{value:T(i),enumerable:!0,configurable:!0,writable:!0});return r}}function G(e,t){return new F(e,t)}class g{values=new Map;cloneValues;constructor(e={}){this.cloneValues=e.cloneValues??!0}clone(e){return this.cloneValues?o(e):e}async get(e,t){let r=this.values.get(t)?.get(e);return r===void 0?void 0:this.clone(r)}async set(e,t,r){let i=this.values.get(t);if(!i)i=new Map,this.values.set(t,i);i.set(e,this.clone(r))}async delete(e,t){let r=this.values.get(t);if(r?.delete(e),r?.size===0)this.values.delete(t)}async deleteForAllScopes(e){if(!e){this.values.clear();return}let t=new Set(e);for(let[r,i]of this.values){for(let a of t)i.delete(a);if(i.size===0)this.values.delete(r)}}async clear(){this.values.clear()}async stored(e){let t={};for(let[r,i]of this.values.get(e)??[])Object.defineProperty(t,r,{value:this.clone(i),enumerable:!0,configurable:!0,writable:!0});return t}}function re(e){return new g(e)}import{createHash as L}from"crypto";function v(e){if(new TextEncoder().encode(e).byteLength<=512)return e;return`sha256:${L("sha256").update(e).digest("hex")}`}function b(e,t){if(e===null||typeof e!=="object"){if(typeof e==="bigint")return{$bigint:e.toString()};if(typeof e==="symbol"||typeof e==="function"||e===void 0)throw new u(`Feature scope contains unsupported value '${typeof e}'.`);if(typeof e==="number"&&!Number.isFinite(e))throw new u("Feature scope cannot contain NaN or Infinity.");return e}if(t.has(e))throw new u("Feature scope cannot contain circular references.");t.add(e);try{if(e instanceof Date)return{$date:e.toISOString()};if(Array.isArray(e))return e.map((a)=>b(a,t));let r=Object.getPrototypeOf(e);if(r!==Object.prototype&&r!==null)throw new u("Feature scope objects must be plain objects or expose featureFlagScope.");let i={};for(let a of Object.keys(e).sort())i[a]=b(e[a],t);return i}finally{t.delete(e)}}function U(e){return typeof e.featureFlagScope==="function"?e.featureFlagScope():e.featureFlagScope}function C(e){return(typeof e.featureFlagType==="function"?e.featureFlagType():e.featureFlagType)?.trim()||e.constructor?.name||"object"}function j(e,t){let r=typeof e.featureFlagType==="function"?e.featureFlagType():e.featureFlagType;if(typeof r==="string"&&r.trim())return r.trim();let i=e._definition;if(i&&typeof i==="object"){let a=i.name;if(typeof a==="string"&&a.trim())return a.trim()}return t.constructor?.name||"object"}function E(e){if(e===null||e===void 0)return"global";if(typeof e==="string")return v(`string:${e}`);if(typeof e==="number"){if(!Number.isFinite(e))throw new u("Feature scope cannot be NaN or Infinity.");return`number:${e}`}if(typeof e==="bigint")return`bigint:${e}`;if(typeof e==="boolean")return`boolean:${e}`;if(typeof e==="symbol"||typeof e==="function")throw new u(`Feature scope of type '${typeof e}' is not supported.`);let t=e;if("featureFlagScope"in t){let r=t,i=U(r);if(typeof i!=="string"&&typeof i!=="number"||String(i).length===0)throw new u("featureFlagScope must resolve to a non-empty string or number.");return v(`model:${C(r)}:${String(i)}`)}if("id"in t&&(typeof t.id==="string"||typeof t.id==="number")){let r=j(t,e);return v(`model:${r}:${String(t.id)}`)}return v(`object:${JSON.stringify(b(e,new WeakSet))}`)}function f(e){if(typeof e!=="string"||e.trim().length===0)throw TypeError("Feature flag names must be non-empty strings.");if(e!==e.trim())throw TypeError("Feature flag names cannot start or end with whitespace.");if(new TextEncoder().encode(e).byteLength>191)throw TypeError("Feature flag names cannot exceed 191 UTF-8 bytes.");return e}function D(e,t){return JSON.stringify([e,t])}function x(e){return typeof e==="function"?e:o(e)}function V(e,t,r){Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0})}class N{manager;scope;constructor(e,t){this.manager=e;this.scope=t}value(e){return this.manager.value(e,this.scope)}values(e){return this.manager.values(e,this.scope)}all(){return this.manager.all(this.scope)}active(e){return this.manager.active(e,this.scope)}inactive(e){return this.manager.inactive(e,this.scope)}activate(e,t=!0){return this.manager.activate(e,t,this.scope)}deactivate(e){return this.manager.deactivate(e,this.scope)}forget(e){return this.manager.forget(e,this.scope)}when(e,t,r){return this.manager.when(e,t,r,this.scope)}unless(e,t,r){return this.manager.unless(e,t,r,this.scope)}}class h{definitions=new Map;pending=new Map;listeners=new Set;resolveScope;driverSource;driverPromise;missingBehavior;constructor(e={}){this.driverSource=e.driver??new g,this.missingBehavior=e.missing??"false",this.resolveScope=e.scopeResolver??E}use(e){return this.driverSource=e,this.driverPromise=void 0,this.pending.clear(),this}missing(e){return this.missingBehavior=e,this}define(e,t){if(typeof e==="string"){if(t===void 0)throw TypeError(`A definition is required for feature '${e}'.`);return this.definitions.set(f(e),x(t)),this}for(let[r,i]of Object.entries(e))this.definitions.set(f(r),x(i));return this}defined(e){return this.definitions.has(f(e))}definedNames(){return[...this.definitions.keys()]}clearDefinitions(){return this.definitions.clear(),this}for(e){return new N(this,e)}async value(e,t=null){let r=f(e),i=this.resolveScope(t),a=D(r,i),n=this.pending.get(a);if(n)return n;let s=this.evaluate(r,t,i);this.pending.set(a,s);try{return await s}finally{if(this.pending.get(a)===s)this.pending.delete(a)}}async evaluate(e,t,r){let i=await this.driver(),a=await i.get(e,r);if(a!==void 0){let d=o(a);return await this.notify({name:e,scope:t,scopeKey:r,value:d,source:"stored"}),d}let n=this.definitions.get(e);if(n===void 0){if(this.missingBehavior==="throw")throw new y(e);let d=!1;return await this.notify({name:e,scope:t,scopeKey:r,value:d,source:"missing"}),d}let s=typeof n==="function"?await n(t,{name:e,scope:t,scopeKey:r}):n,p=o(s);return await i.set(e,r,p),await this.notify({name:e,scope:t,scopeKey:r,value:p,source:"resolver"}),p}async values(e,t=null){let r=[...new Set(e.map(f))],i=await Promise.all(r.map(async(a)=>[a,await this.value(a,t)]));return Object.fromEntries(i)}async all(e=null){let t=this.resolveScope(e),r=await(await this.driver()).stored(t),i={};for(let[a,n]of Object.entries(r))V(i,a,o(n));for(let a of this.definitions.keys())if(!Object.hasOwn(i,a))V(i,a,await this.value(a,e));return i}async active(e,t=null){return Boolean(await this.value(e,t))}async inactive(e,t=null){return!await this.active(e,t)}async activate(e,t=!0,r=null){let i=f(e),a=this.resolveScope(r);await this.awaitPending(i,a),await(await this.driver()).set(i,a,o(t))}deactivate(e,t=null){return this.activate(e,!1,t)}async forget(e,t=null){let r=f(e),i=this.resolveScope(t);await this.awaitPending(r,i),await(await this.driver()).delete(r,i)}async purge(e){let t=e===void 0?void 0:(Array.isArray(e)?e:[e]).map(f);await Promise.allSettled(this.pending.values()),await(await this.driver()).deleteForAllScopes(t)}async flush(){await Promise.allSettled(this.pending.values()),await(await this.driver()).clear()}async when(e,t,r,i=null){let a=await this.value(e,i);return Boolean(a)?t(a):r?.(a)}async unless(e,t,r,i=null){let a=await this.value(e,i);return Boolean(a)?r?.(a):t(a)}onEvaluated(e){return this.listeners.add(e),()=>this.listeners.delete(e)}async notify(e){await Promise.allSettled([...this.listeners].map((t)=>Promise.resolve().then(()=>t(e))))}async awaitPending(e,t){let r=this.pending.get(D(e,t));if(r)try{await r}catch{}}driver(){if(!this.driverPromise){let e=Promise.resolve(typeof this.driverSource==="function"?this.driverSource():this.driverSource);this.driverPromise=e,e.catch(()=>{if(this.driverPromise===e)this.driverPromise=void 0})}return this.driverPromise}}function fe(e){return new h(e)}function q(e){if(!Number.isFinite(e)||e<0||e>100)throw RangeError(`Percentage must be between 0 and 100. Received ${e}.`)}function R(e){let t=new TextEncoder().encode(e),r=2166136261;for(let i of t)r^=i,r=Math.imul(r,16777619);return(r>>>0)/4294967296*100}function pe(e){return q(e),(t,r)=>R(`${r.name}\x00${r.scopeKey}`)<e}function ge(e){let t=Object.entries(e),r=t[t.length-1];if(!r)throw RangeError("At least one feature variant is required.");let i=0;for(let[a,n]of t){if(!Number.isFinite(n)||n<=0)throw RangeError(`Variant '${a}' must have a positive, finite weight.`);i+=n}return(a,n)=>{let s=R(`${n.name}\x00${n.scopeKey}`)/100*i,p=0;for(let[d,I]of t)if(p+=I,s<p)return d;return r[0]}}async function z(e,t){if(e.default==="database"){if(t!=="sqlite"&&t!=="mysql"&&t!=="singlestore"&&t!=="postgres")throw new l(`The feature flag database driver requires a SQL database. Received '${t??"undefined"}'.`);let{db:r}=await import("@stacksjs/database");return new F(r,{...e.drivers?.database,dialect:t==="mysql"||t==="singlestore"?"mysql":t==="postgres"?"postgres":"sqlite"})}return new g(e.drivers?.memory)}var A=new h({driver:async()=>{let{awaitConfig:e}=await import("@stacksjs/config"),t=await e();return A.missing(t.featureFlags.missing??"false"),z(t.featureFlags,t.database.default)}}),we=A;export{F as DatabaseFeatureFlagDriver,A as Feature,m as FeatureFlagError,h as FeatureFlagManager,l as FeatureFlagStoreError,y as FeatureNotDefinedError,u as InvalidFeatureScopeError,c as InvalidFeatureValueError,g as MemoryFeatureFlagDriver,N as ScopedFeatureFlags,G as createDatabaseFeatureFlagDriver,fe as createFeatureFlags,re as createMemoryFeatureFlagDriver,P as ensureFeatureFlagTable,R as featureBucket,O as featureFlagMigrationSql,w as featureFlagTableName,we as featureFlags,E as featureScopeKey,o as normalizeFeatureValue,pe as percentage,ge as variants};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/feature-flags",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.74.
|
|
5
|
+
"version": "0.74.35",
|
|
6
6
|
"description": "Typed, scoped feature flags for Stacks applications.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"prepublishOnly": "bun run build"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@stacksjs/config": "0.74.
|
|
62
|
-
"@stacksjs/database": "0.74.
|
|
61
|
+
"@stacksjs/config": "0.74.35",
|
|
62
|
+
"@stacksjs/database": "0.74.35"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@stacksjs/query-builder": "0.74.
|
|
65
|
+
"@stacksjs/query-builder": "0.74.35",
|
|
66
66
|
"better-dx": "^0.2.24"
|
|
67
67
|
}
|
|
68
68
|
}
|