@gradual-so/sdk 0.3.0 → 0.4.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 +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';function d(a,t){let{attributeKey:i,operator:r,value:n}=a,e=t[i];switch(r){case "equals":return e===n;case "not_equals":return e!==n;case "contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?e.includes(n):false;case "not_contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?!e.includes(n):true;case "starts_with":return typeof e=="string"&&typeof n=="string"?e.startsWith(n):false;case "ends_with":return typeof e=="string"&&typeof n=="string"?e.endsWith(n):false;case "greater_than":return typeof e=="number"&&typeof n=="number"?e>n:false;case "less_than":return typeof e=="number"&&typeof n=="number"?e<n:false;case "greater_than_or_equal":return typeof e=="number"&&typeof n=="number"?e>=n:false;case "less_than_or_equal":return typeof e=="number"&&typeof n=="number"?e<=n:false;case "in":return Array.isArray(n)?n.includes(e):false;case "not_in":return Array.isArray(n)?!n.includes(e):true;case "exists":return e!=null;case "not_exists":return e==null;default:return false}}function l(a,t){return a.every(i=>d(i,t))}function p(a,t){return l(a.conditions,t)}function h(a,t,i){switch(a.type){case "individual":return a.attributeKey&&a.attributeValue!==void 0?t[a.attributeKey]===a.attributeValue:false;case "rule":return a.conditions?l(a.conditions,t):false;case "segment":if(a.segmentKey){let r=i[a.segmentKey];if(r)return p(r,t)}return false;default:return false}}function s(a,t,i){if(!a.enabled)return a.variations[a.offVariationKey]?.value;let r=[...a.targets].sort((e,o)=>e.sortOrder-o.sortOrder);for(let e of r)if(h(e,t,i)){let o=a.variations[e.variationKey];if(o)return o.value}return a.variations[a.defaultVariationKey]?.value}var f="https://worker.gradual.so/api/v1",u=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??f,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};}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 n=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${n.error??t.statusText}`)}let i=await t.json();if(!i.valid)throw new Error(`Gradual: Invalid API key - ${i.error??"Unknown error"}`);let r=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${n.error??r.statusText}`)}this.snapshot=await r.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){return {...this.identifiedContext,...t?.context}}evaluate(t,i){let r=this.ensureReady()
|
|
1
|
+
'use strict';function d(a,t){let{attributeKey:i,operator:r,value:n}=a,e=t[i];switch(r){case "equals":return e===n;case "not_equals":return e!==n;case "contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?e.includes(n):false;case "not_contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?!e.includes(n):true;case "starts_with":return typeof e=="string"&&typeof n=="string"?e.startsWith(n):false;case "ends_with":return typeof e=="string"&&typeof n=="string"?e.endsWith(n):false;case "greater_than":return typeof e=="number"&&typeof n=="number"?e>n:false;case "less_than":return typeof e=="number"&&typeof n=="number"?e<n:false;case "greater_than_or_equal":return typeof e=="number"&&typeof n=="number"?e>=n:false;case "less_than_or_equal":return typeof e=="number"&&typeof n=="number"?e<=n:false;case "in":return Array.isArray(n)?n.includes(e):false;case "not_in":return Array.isArray(n)?!n.includes(e):true;case "exists":return e!=null;case "not_exists":return e==null;default:return false}}function l(a,t){return a.every(i=>d(i,t))}function p(a,t){return l(a.conditions,t)}function h(a,t,i){switch(a.type){case "individual":return a.attributeKey&&a.attributeValue!==void 0?t[a.attributeKey]===a.attributeValue:false;case "rule":return a.conditions?l(a.conditions,t):false;case "segment":if(a.segmentKey){let r=i[a.segmentKey];if(r)return p(r,t)}return false;default:return false}}function s(a,t,i){if(!a.enabled)return a.variations[a.offVariationKey]?.value;let r=[...a.targets].sort((e,o)=>e.sortOrder-o.sortOrder);for(let e of r)if(h(e,t,i)){let o=a.variations[e.variationKey];if(o)return o.value}return a.variations[a.defaultVariationKey]?.value}var f="https://worker.gradual.so/api/v1",u=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??f,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};}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 n=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${n.error??t.statusText}`)}let i=await t.json();if(!i.valid)throw new Error(`Gradual: Invalid API key - ${i.error??"Unknown error"}`);let r=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${n.error??r.statusText}`)}this.snapshot=await r.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){return {...this.identifiedContext,...t?.context}}evaluate(t,i){let r=this.ensureReady();if(!r.flags)return;let n=r.flags[t];if(n)return s(n,i,r.segments??{})}async ready(){await this.initPromise;}isReady(){return this.snapshot!==null}async isEnabled(t,i){await this.initPromise;let r=this.evaluate(t,this.mergeContext(i));return typeof r=="boolean"?r:false}async get(t,i){await this.initPromise;let r=this.evaluate(t,this.mergeContext(i));return r??i.fallback}isEnabledSync(t,i){let r=this.evaluate(t,this.mergeContext(i));return typeof r=="boolean"?r:false}getSync(t,i){let r=this.evaluate(t,this.mergeContext(i));return r??i.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 i=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to refresh - ${i.error??t.statusText}`)}this.snapshot=await t.json();}getSnapshot(){return this.snapshot}};function c(a){return new u(a)}
|
|
2
2
|
exports.createGradual=c;exports.evaluateFlag=s;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/evaluator.ts","../src/client.ts"],"names":["evaluateCondition","condition","context","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","evaluateFlag","flag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","initResponse","error","initData","snapshotResponse","key","snapshot","response","createGradual"],"mappings":"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","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 { attributeKey, operator, value } = condition;\n const contextValue = context[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 (target.attributeKey && target.attributeValue !== undefined) {\n return context[target.attributeKey] === target.attributeValue;\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\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 return { ...this.identifiedContext, ...options?.context };\n }\n\n private evaluate(key: string, context: EvaluationContext): unknown {\n const snapshot = this.ensureReady();\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":["evaluateCondition","condition","context","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","evaluateFlag","flag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","initResponse","error","initData","snapshotResponse","key","snapshot","response","createGradual"],"mappings":"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","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 { attributeKey, operator, value } = condition;\n const contextValue = context[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 (target.attributeKey && target.attributeValue !== undefined) {\n return context[target.attributeKey] === target.attributeValue;\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\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 return { ...this.identifiedContext, ...options?.context };\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"]}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
function d(a,t){let{attributeKey:i,operator:r,value:n}=a,e=t[i];switch(r){case "equals":return e===n;case "not_equals":return e!==n;case "contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?e.includes(n):false;case "not_contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?!e.includes(n):true;case "starts_with":return typeof e=="string"&&typeof n=="string"?e.startsWith(n):false;case "ends_with":return typeof e=="string"&&typeof n=="string"?e.endsWith(n):false;case "greater_than":return typeof e=="number"&&typeof n=="number"?e>n:false;case "less_than":return typeof e=="number"&&typeof n=="number"?e<n:false;case "greater_than_or_equal":return typeof e=="number"&&typeof n=="number"?e>=n:false;case "less_than_or_equal":return typeof e=="number"&&typeof n=="number"?e<=n:false;case "in":return Array.isArray(n)?n.includes(e):false;case "not_in":return Array.isArray(n)?!n.includes(e):true;case "exists":return e!=null;case "not_exists":return e==null;default:return false}}function l(a,t){return a.every(i=>d(i,t))}function p(a,t){return l(a.conditions,t)}function h(a,t,i){switch(a.type){case "individual":return a.attributeKey&&a.attributeValue!==void 0?t[a.attributeKey]===a.attributeValue:false;case "rule":return a.conditions?l(a.conditions,t):false;case "segment":if(a.segmentKey){let r=i[a.segmentKey];if(r)return p(r,t)}return false;default:return false}}function s(a,t,i){if(!a.enabled)return a.variations[a.offVariationKey]?.value;let r=[...a.targets].sort((e,o)=>e.sortOrder-o.sortOrder);for(let e of r)if(h(e,t,i)){let o=a.variations[e.variationKey];if(o)return o.value}return a.variations[a.defaultVariationKey]?.value}var f="https://worker.gradual.so/api/v1",u=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??f,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};}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 n=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${n.error??t.statusText}`)}let i=await t.json();if(!i.valid)throw new Error(`Gradual: Invalid API key - ${i.error??"Unknown error"}`);let r=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${n.error??r.statusText}`)}this.snapshot=await r.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){return {...this.identifiedContext,...t?.context}}evaluate(t,i){let r=this.ensureReady()
|
|
1
|
+
function d(a,t){let{attributeKey:i,operator:r,value:n}=a,e=t[i];switch(r){case "equals":return e===n;case "not_equals":return e!==n;case "contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?e.includes(n):false;case "not_contains":return typeof e=="string"&&typeof n=="string"||Array.isArray(e)?!e.includes(n):true;case "starts_with":return typeof e=="string"&&typeof n=="string"?e.startsWith(n):false;case "ends_with":return typeof e=="string"&&typeof n=="string"?e.endsWith(n):false;case "greater_than":return typeof e=="number"&&typeof n=="number"?e>n:false;case "less_than":return typeof e=="number"&&typeof n=="number"?e<n:false;case "greater_than_or_equal":return typeof e=="number"&&typeof n=="number"?e>=n:false;case "less_than_or_equal":return typeof e=="number"&&typeof n=="number"?e<=n:false;case "in":return Array.isArray(n)?n.includes(e):false;case "not_in":return Array.isArray(n)?!n.includes(e):true;case "exists":return e!=null;case "not_exists":return e==null;default:return false}}function l(a,t){return a.every(i=>d(i,t))}function p(a,t){return l(a.conditions,t)}function h(a,t,i){switch(a.type){case "individual":return a.attributeKey&&a.attributeValue!==void 0?t[a.attributeKey]===a.attributeValue:false;case "rule":return a.conditions?l(a.conditions,t):false;case "segment":if(a.segmentKey){let r=i[a.segmentKey];if(r)return p(r,t)}return false;default:return false}}function s(a,t,i){if(!a.enabled)return a.variations[a.offVariationKey]?.value;let r=[...a.targets].sort((e,o)=>e.sortOrder-o.sortOrder);for(let e of r)if(h(e,t,i)){let o=a.variations[e.variationKey];if(o)return o.value}return a.variations[a.defaultVariationKey]?.value}var f="https://worker.gradual.so/api/v1",u=class{apiKey;environment;baseUrl;initPromise;snapshot=null;identifiedContext={};sync;constructor(t){this.apiKey=t.apiKey,this.environment=t.environment,this.baseUrl=t.baseUrl??f,this.initPromise=this.init(),this.sync={isEnabled:this.isEnabledSync.bind(this),get:this.getSync.bind(this)};}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 n=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to initialize - ${n.error??t.statusText}`)}let i=await t.json();if(!i.valid)throw new Error(`Gradual: Invalid API key - ${i.error??"Unknown error"}`);let r=await fetch(`${this.baseUrl}/sdk/snapshot?environment=${encodeURIComponent(this.environment)}`,{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(`Gradual: Failed to fetch snapshot - ${n.error??r.statusText}`)}this.snapshot=await r.json();}ensureReady(){if(!this.snapshot)throw new Error("Gradual: SDK not ready. Use await ready() or async methods.");return this.snapshot}mergeContext(t){return {...this.identifiedContext,...t?.context}}evaluate(t,i){let r=this.ensureReady();if(!r.flags)return;let n=r.flags[t];if(n)return s(n,i,r.segments??{})}async ready(){await this.initPromise;}isReady(){return this.snapshot!==null}async isEnabled(t,i){await this.initPromise;let r=this.evaluate(t,this.mergeContext(i));return typeof r=="boolean"?r:false}async get(t,i){await this.initPromise;let r=this.evaluate(t,this.mergeContext(i));return r??i.fallback}isEnabledSync(t,i){let r=this.evaluate(t,this.mergeContext(i));return typeof r=="boolean"?r:false}getSync(t,i){let r=this.evaluate(t,this.mergeContext(i));return r??i.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 i=await t.json().catch(()=>({}));throw new Error(`Gradual: Failed to refresh - ${i.error??t.statusText}`)}this.snapshot=await t.json();}getSnapshot(){return this.snapshot}};function c(a){return new u(a)}
|
|
2
2
|
export{c 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","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","evaluateFlag","flag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","initResponse","error","initData","snapshotResponse","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 { attributeKey, operator, value } = condition;\n const contextValue = context[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 (target.attributeKey && target.attributeValue !== undefined) {\n return context[target.attributeKey] === target.attributeValue;\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\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 return { ...this.identifiedContext, ...options?.context };\n }\n\n private evaluate(key: string, context: EvaluationContext): unknown {\n const snapshot = this.ensureReady();\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":["evaluateCondition","condition","context","attributeKey","operator","value","contextValue","evaluateConditions","conditions","evaluateSegment","segment","evaluateTarget","target","segments","evaluateFlag","flag","sortedTargets","a","b","variation","DEFAULT_BASE_URL","GradualClient","options","initResponse","error","initData","snapshotResponse","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 { attributeKey, operator, value } = condition;\n const contextValue = context[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 (target.attributeKey && target.attributeValue !== undefined) {\n return context[target.attributeKey] === target.attributeValue;\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\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 return { ...this.identifiedContext, ...options?.context };\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"]}
|