@edgeflags/sdk 0.1.0
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 +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +82 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';var a=class extends Error{statusCode;constructor(e,t){super(e),this.name="EdgeFlagsError",this.statusCode=t;}};var g=class{enabled;constructor(e){this.enabled=e;}debug(e,...t){this.enabled&&console.debug(`[EdgeFlags] ${e}`,...t);}warn(e,...t){this.enabled&&console.warn(`[EdgeFlags] ${e}`,...t);}error(e,...t){this.enabled&&console.error(`[EdgeFlags] ${e}`,...t);}};var c=class{listeners=new Map;on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{this.listeners.get(e)?.delete(t);}}emit(e,t){let r=this.listeners.get(e);if(r)for(let s of r)s(t);}removeAll(){this.listeners.clear();}};var u=class{flags=new Map;configs=new Map;getFlag(e){return this.flags.get(e)}getConfig(e){return this.configs.get(e)}allFlags(){return Object.fromEntries(this.flags)}allConfigs(){return Object.fromEntries(this.configs)}update(e,t){let r={flags:[],configs:[]};for(let[s,i]of Object.entries(e)){let o=this.flags.get(s);f(o,i)||r.flags.push({key:s,previous:o,current:i}),this.flags.set(s,i);}for(let[s,i]of Object.entries(t)){let o=this.configs.get(s);f(o,i)||r.configs.push({key:s,previous:o,current:i}),this.configs.set(s,i);}return r.flags.length===0&&r.configs.length===0?null:r}seed(e,t){for(let[r,s]of Object.entries(e))this.flags.set(r,s);for(let[r,s]of Object.entries(t))this.configs.set(r,s);}clear(){this.flags.clear(),this.configs.clear();}};function f(n,e){if(n===e)return true;if(n===null||e===null||n===void 0||e===void 0||typeof n!=typeof e||typeof n!="object")return false;let t=Object.keys(n),r=Object.keys(e);if(t.length!==r.length)return false;for(let s of t)if(!f(n[s],e[s]))return false;return true}var d=class{baseUrl;token;constructor(e,t){this.baseUrl=e.replace(/\/$/,""),this.token=t;}async fetchAll(e){let t=await fetch(`${this.baseUrl}/api/v1/evaluate`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify({context:e})});if(!t.ok)throw new a(`Evaluation request failed: ${t.status} ${t.statusText}`,t.status);return await t.json()}};var h=class{intervalMs;timer=null;task;onError;constructor(e,t,r){this.intervalMs=e,this.task=t,this.onError=r;}start(){this.timer||(this.timer=setInterval(async()=>{try{await this.task();}catch(e){this.onError(e instanceof Error?e:new Error(String(e)));}},this.intervalMs));}stop(){this.timer&&(clearInterval(this.timer),this.timer=null);}get running(){return this.timer!==null}};var p={custom:{}},v=6e4,l=class{cache;emitter;logger;fetcher;poller=null;context;pollingInterval;ready=false;mockData;constructor(e){let t=e._mock??null;this.mockData=t,this.cache=new u,this.emitter=new c,this.logger=new g(e.debug??false),this.context=e.context??{...p},this.pollingInterval=e.pollingInterval??v,t?(this.fetcher=null,this.cache.seed(t.flags,t.configs),this.ready=true,this.logger.debug("Mock client created")):(this.fetcher=new d(e.baseUrl,e.token),e.bootstrap&&(this.cache.seed(e.bootstrap.flags??{},e.bootstrap.configs??{}),this.logger.debug("Bootstrap data loaded")));}async init(){if(this.mockData){this.emitter.emit("ready",void 0);return}try{await this.fetchAndSeed(),this.ready=!0,this.logger.debug("Initialized"),this.emitter.emit("ready",void 0),this.poller=new h(this.pollingInterval,()=>this.refresh(),e=>{this.logger.error("Polling error",e),this.emitter.emit("error",e);}),this.poller.start(),this.logger.debug(`Polling started (${this.pollingInterval}ms)`);}catch(e){let t=e instanceof Error?e:new Error(String(e));if(this.logger.error("Init failed",t),this.emitter.emit("error",t),this.cache.allFlags()&&Object.keys(this.cache.allFlags()).length>0)this.ready=true,this.logger.warn("Using bootstrap data after init failure"),this.emitter.emit("ready",void 0);else throw t}}flag(e,t){let r=this.cache.getFlag(e);return r===void 0?t:r}config(e,t){let r=this.cache.getConfig(e);return r===void 0?t:r}allFlags(){return this.cache.allFlags()}allConfigs(){return this.cache.allConfigs()}async identify(e){this.context=e,this.logger.debug("Context updated",e),this.ready&&this.fetcher&&await this.refresh();}async refresh(){if(!this.fetcher)return;this.logger.debug("Fetching evaluations");let e=await this.fetcher.fetchAll(this.context),t=this.cache.update(e.flags,e.configs);t&&(this.logger.debug("Changes detected",t),this.emitter.emit("change",t));}async fetchAndSeed(){if(!this.fetcher)return;this.logger.debug("Fetching initial evaluations");let e=await this.fetcher.fetchAll(this.context);this.cache.seed(e.flags,e.configs);}on(e,t){return this.emitter.on(e,t)}get isReady(){return this.ready}destroy(){this.poller?.stop(),this.poller=null,this.cache.clear(),this.emitter.removeAll(),this.ready=false,this.logger.debug("Destroyed");}};function E(n={}){return new l({token:"mock_token",baseUrl:"http://localhost",_mock:{flags:n.flags??{},configs:n.configs??{}}})}
|
|
2
|
+
exports.EdgeFlags=l;exports.EdgeFlagsError=a;exports.createMockClient=E;//# sourceMappingURL=index.cjs.map
|
|
3
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/logger.ts","../src/emitter.ts","../src/cache.ts","../src/fetcher.ts","../src/poller.ts","../src/client.ts","../src/mock.ts"],"names":["EdgeFlagsError","message","statusCode","Logger","enabled","args","Emitter","event","fn","payload","fns","Cache","key","flags","configs","changes","current","previous","deepEqual","value","a","b","keysA","keysB","Fetcher","baseUrl","token","context","res","Poller","intervalMs","task","onError","err","DEFAULT_CONTEXT","DEFAULT_POLL_INTERVAL","EdgeFlags","options","mock","error","defaultValue","data","createMockClient"],"mappings":"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","file":"index.cjs","sourcesContent":["export class EdgeFlagsError extends Error {\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'EdgeFlagsError';\n this.statusCode = statusCode;\n }\n}\n","export class Logger {\n private enabled: boolean;\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.enabled) {\n console.debug(`[EdgeFlags] ${message}`, ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.enabled) {\n console.warn(`[EdgeFlags] ${message}`, ...args);\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.enabled) {\n console.error(`[EdgeFlags] ${message}`, ...args);\n }\n }\n}\n","import type { EdgeFlagsEvent, EventPayloadMap } from './types.js';\n\ntype Listener<E extends EdgeFlagsEvent> = (payload: EventPayloadMap[E]) => void;\n\nexport class Emitter {\n private listeners = new Map<EdgeFlagsEvent, Set<Listener<never>>>();\n\n on<E extends EdgeFlagsEvent>(event: E, fn: Listener<E>): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(fn as Listener<never>);\n\n return () => {\n this.listeners.get(event)?.delete(fn as Listener<never>);\n };\n }\n\n emit<E extends EdgeFlagsEvent>(event: E, payload: EventPayloadMap[E]): void {\n const fns = this.listeners.get(event);\n if (!fns) return;\n for (const fn of fns) {\n (fn as Listener<E>)(payload);\n }\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n","import type { FlagValue, ChangeEvent } from './types.js';\n\nexport class Cache {\n private flags = new Map<string, FlagValue>();\n private configs = new Map<string, unknown>();\n\n getFlag(key: string): FlagValue | undefined {\n return this.flags.get(key);\n }\n\n getConfig(key: string): unknown {\n return this.configs.get(key);\n }\n\n allFlags(): Record<string, FlagValue> {\n return Object.fromEntries(this.flags);\n }\n\n allConfigs(): Record<string, unknown> {\n return Object.fromEntries(this.configs);\n }\n\n update(\n flags: Record<string, FlagValue>,\n configs: Record<string, unknown>,\n ): ChangeEvent | null {\n const changes: ChangeEvent = { flags: [], configs: [] };\n\n for (const [key, current] of Object.entries(flags)) {\n const previous = this.flags.get(key);\n if (!deepEqual(previous, current)) {\n changes.flags.push({ key, previous, current });\n }\n this.flags.set(key, current);\n }\n\n for (const [key, current] of Object.entries(configs)) {\n const previous = this.configs.get(key);\n if (!deepEqual(previous, current)) {\n changes.configs.push({ key, previous, current });\n }\n this.configs.set(key, current);\n }\n\n if (changes.flags.length === 0 && changes.configs.length === 0) {\n return null;\n }\n return changes;\n }\n\n seed(flags: Record<string, FlagValue>, configs: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(flags)) {\n this.flags.set(key, value);\n }\n for (const [key, value] of Object.entries(configs)) {\n this.configs.set(key, value);\n }\n }\n\n clear(): void {\n this.flags.clear();\n this.configs.clear();\n }\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null || a === undefined || b === undefined) return false;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n const keysA = Object.keys(a as Record<string, unknown>);\n const keysB = Object.keys(b as Record<string, unknown>);\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false;\n }\n }\n return true;\n}\n","import type { EvaluationContext, EvaluationResponse } from './types.js';\nimport { EdgeFlagsError } from './errors.js';\n\nexport class Fetcher {\n private baseUrl: string;\n private token: string;\n\n constructor(baseUrl: string, token: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.token = token;\n }\n\n async fetchAll(context: EvaluationContext): Promise<EvaluationResponse> {\n const res = await fetch(`${this.baseUrl}/api/v1/evaluate`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ context }),\n });\n\n if (!res.ok) {\n throw new EdgeFlagsError(\n `Evaluation request failed: ${res.status} ${res.statusText}`,\n res.status,\n );\n }\n\n return (await res.json()) as EvaluationResponse;\n }\n}\n","export class Poller {\n private intervalMs: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n private task: () => Promise<void>;\n private onError: (err: Error) => void;\n\n constructor(\n intervalMs: number,\n task: () => Promise<void>,\n onError: (err: Error) => void,\n ) {\n this.intervalMs = intervalMs;\n this.task = task;\n this.onError = onError;\n }\n\n start(): void {\n if (this.timer) return;\n this.timer = setInterval(async () => {\n try {\n await this.task();\n } catch (err) {\n this.onError(err instanceof Error ? err : new Error(String(err)));\n }\n }, this.intervalMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n get running(): boolean {\n return this.timer !== null;\n }\n}\n","import type {\n FlagValue,\n EvaluationContext,\n EdgeFlagsOptions,\n EdgeFlagsEvent,\n EventPayloadMap,\n ChangeEvent,\n} from './types.js';\nimport { EdgeFlagsError } from './errors.js';\nimport { Logger } from './logger.js';\nimport { Emitter } from './emitter.js';\nimport { Cache } from './cache.js';\nimport { Fetcher } from './fetcher.js';\nimport { Poller } from './poller.js';\n\ninterface MockData {\n flags: Record<string, FlagValue>;\n configs: Record<string, unknown>;\n}\n\nconst DEFAULT_CONTEXT: EvaluationContext = { custom: {} };\nconst DEFAULT_POLL_INTERVAL = 60_000;\n\nexport class EdgeFlags {\n private cache: Cache;\n private emitter: Emitter;\n private logger: Logger;\n private fetcher: Fetcher | null;\n private poller: Poller | null = null;\n private context: EvaluationContext;\n private pollingInterval: number;\n private ready = false;\n private mockData: MockData | null;\n\n constructor(options: EdgeFlagsOptions) {\n const mock = (options as EdgeFlagsOptions & { _mock?: MockData })._mock ?? null;\n this.mockData = mock;\n this.cache = new Cache();\n this.emitter = new Emitter();\n this.logger = new Logger(options.debug ?? false);\n this.context = options.context ?? { ...DEFAULT_CONTEXT };\n this.pollingInterval = options.pollingInterval ?? DEFAULT_POLL_INTERVAL;\n\n if (mock) {\n this.fetcher = null;\n this.cache.seed(mock.flags, mock.configs);\n this.ready = true;\n this.logger.debug('Mock client created');\n } else {\n this.fetcher = new Fetcher(options.baseUrl, options.token);\n if (options.bootstrap) {\n this.cache.seed(\n options.bootstrap.flags ?? {},\n options.bootstrap.configs ?? {},\n );\n this.logger.debug('Bootstrap data loaded');\n }\n }\n }\n\n async init(): Promise<void> {\n if (this.mockData) {\n this.emitter.emit('ready', undefined);\n return;\n }\n\n try {\n await this.fetchAndSeed();\n this.ready = true;\n this.logger.debug('Initialized');\n this.emitter.emit('ready', undefined);\n\n this.poller = new Poller(\n this.pollingInterval,\n () => this.refresh(),\n (err) => {\n this.logger.error('Polling error', err);\n this.emitter.emit('error', err);\n },\n );\n this.poller.start();\n this.logger.debug(`Polling started (${this.pollingInterval}ms)`);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.logger.error('Init failed', error);\n this.emitter.emit('error', error);\n\n if (this.cache.allFlags() && Object.keys(this.cache.allFlags()).length > 0) {\n this.ready = true;\n this.logger.warn('Using bootstrap data after init failure');\n this.emitter.emit('ready', undefined);\n } else {\n throw error;\n }\n }\n }\n\n flag(key: string): FlagValue | undefined;\n flag<T extends FlagValue>(key: string, defaultValue: T): T;\n flag(key: string, defaultValue?: FlagValue): FlagValue | undefined {\n const value = this.cache.getFlag(key);\n if (value === undefined) return defaultValue;\n return value;\n }\n\n config(key: string): unknown;\n config<T>(key: string, defaultValue: T): T;\n config(key: string, defaultValue?: unknown): unknown {\n const value = this.cache.getConfig(key);\n if (value === undefined) return defaultValue;\n return value;\n }\n\n allFlags(): Record<string, FlagValue> {\n return this.cache.allFlags();\n }\n\n allConfigs(): Record<string, unknown> {\n return this.cache.allConfigs();\n }\n\n async identify(context: EvaluationContext): Promise<void> {\n this.context = context;\n this.logger.debug('Context updated', context);\n if (this.ready && this.fetcher) {\n await this.refresh();\n }\n }\n\n async refresh(): Promise<void> {\n if (!this.fetcher) return;\n\n this.logger.debug('Fetching evaluations');\n const data = await this.fetcher.fetchAll(this.context);\n const changes = this.cache.update(data.flags, data.configs);\n\n if (changes) {\n this.logger.debug('Changes detected', changes);\n this.emitter.emit('change', changes);\n }\n }\n\n private async fetchAndSeed(): Promise<void> {\n if (!this.fetcher) return;\n\n this.logger.debug('Fetching initial evaluations');\n const data = await this.fetcher.fetchAll(this.context);\n this.cache.seed(data.flags, data.configs);\n }\n\n on<E extends EdgeFlagsEvent>(\n event: E,\n fn: (payload: EventPayloadMap[E]) => void,\n ): () => void {\n return this.emitter.on(event, fn);\n }\n\n get isReady(): boolean {\n return this.ready;\n }\n\n destroy(): void {\n this.poller?.stop();\n this.poller = null;\n this.cache.clear();\n this.emitter.removeAll();\n this.ready = false;\n this.logger.debug('Destroyed');\n }\n}\n","import type { FlagValue, EdgeFlagsOptions } from './types.js';\nimport { EdgeFlags } from './client.js';\n\nexport interface MockOptions {\n flags?: Record<string, FlagValue>;\n configs?: Record<string, unknown>;\n}\n\nexport function createMockClient(options: MockOptions = {}): EdgeFlags {\n const client = new EdgeFlags({\n token: 'mock_token',\n baseUrl: 'http://localhost',\n _mock: {\n flags: options.flags ?? {},\n configs: options.configs ?? {},\n },\n } as EdgeFlagsOptions & { _mock: { flags: Record<string, FlagValue>; configs: Record<string, unknown> } });\n\n return client;\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
type FlagValue = boolean | string | number | Record<string, unknown>;
|
|
2
|
+
interface EvaluationContext {
|
|
3
|
+
user_id?: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
plan?: string;
|
|
7
|
+
segments?: string[];
|
|
8
|
+
environment?: string;
|
|
9
|
+
custom: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
interface EvaluationResponse {
|
|
12
|
+
flags: Record<string, FlagValue>;
|
|
13
|
+
configs: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface ChangeEvent {
|
|
16
|
+
flags: {
|
|
17
|
+
key: string;
|
|
18
|
+
previous: FlagValue | undefined;
|
|
19
|
+
current: FlagValue;
|
|
20
|
+
}[];
|
|
21
|
+
configs: {
|
|
22
|
+
key: string;
|
|
23
|
+
previous: unknown;
|
|
24
|
+
current: unknown;
|
|
25
|
+
}[];
|
|
26
|
+
}
|
|
27
|
+
interface EdgeFlagsOptions {
|
|
28
|
+
token: string;
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
context?: EvaluationContext;
|
|
31
|
+
pollingInterval?: number;
|
|
32
|
+
bootstrap?: {
|
|
33
|
+
flags?: Record<string, FlagValue>;
|
|
34
|
+
configs?: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
debug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
type EdgeFlagsEvent = 'ready' | 'change' | 'error';
|
|
39
|
+
type EventPayloadMap = {
|
|
40
|
+
ready: undefined;
|
|
41
|
+
change: ChangeEvent;
|
|
42
|
+
error: Error;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
declare class EdgeFlags {
|
|
46
|
+
private cache;
|
|
47
|
+
private emitter;
|
|
48
|
+
private logger;
|
|
49
|
+
private fetcher;
|
|
50
|
+
private poller;
|
|
51
|
+
private context;
|
|
52
|
+
private pollingInterval;
|
|
53
|
+
private ready;
|
|
54
|
+
private mockData;
|
|
55
|
+
constructor(options: EdgeFlagsOptions);
|
|
56
|
+
init(): Promise<void>;
|
|
57
|
+
flag(key: string): FlagValue | undefined;
|
|
58
|
+
flag<T extends FlagValue>(key: string, defaultValue: T): T;
|
|
59
|
+
config(key: string): unknown;
|
|
60
|
+
config<T>(key: string, defaultValue: T): T;
|
|
61
|
+
allFlags(): Record<string, FlagValue>;
|
|
62
|
+
allConfigs(): Record<string, unknown>;
|
|
63
|
+
identify(context: EvaluationContext): Promise<void>;
|
|
64
|
+
refresh(): Promise<void>;
|
|
65
|
+
private fetchAndSeed;
|
|
66
|
+
on<E extends EdgeFlagsEvent>(event: E, fn: (payload: EventPayloadMap[E]) => void): () => void;
|
|
67
|
+
get isReady(): boolean;
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare class EdgeFlagsError extends Error {
|
|
72
|
+
readonly statusCode: number | undefined;
|
|
73
|
+
constructor(message: string, statusCode?: number);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface MockOptions {
|
|
77
|
+
flags?: Record<string, FlagValue>;
|
|
78
|
+
configs?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
declare function createMockClient(options?: MockOptions): EdgeFlags;
|
|
81
|
+
|
|
82
|
+
export { type ChangeEvent, EdgeFlags, EdgeFlagsError, type EdgeFlagsEvent, type EdgeFlagsOptions, type EvaluationContext, type EvaluationResponse, type EventPayloadMap, type FlagValue, createMockClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
type FlagValue = boolean | string | number | Record<string, unknown>;
|
|
2
|
+
interface EvaluationContext {
|
|
3
|
+
user_id?: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
plan?: string;
|
|
7
|
+
segments?: string[];
|
|
8
|
+
environment?: string;
|
|
9
|
+
custom: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
interface EvaluationResponse {
|
|
12
|
+
flags: Record<string, FlagValue>;
|
|
13
|
+
configs: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface ChangeEvent {
|
|
16
|
+
flags: {
|
|
17
|
+
key: string;
|
|
18
|
+
previous: FlagValue | undefined;
|
|
19
|
+
current: FlagValue;
|
|
20
|
+
}[];
|
|
21
|
+
configs: {
|
|
22
|
+
key: string;
|
|
23
|
+
previous: unknown;
|
|
24
|
+
current: unknown;
|
|
25
|
+
}[];
|
|
26
|
+
}
|
|
27
|
+
interface EdgeFlagsOptions {
|
|
28
|
+
token: string;
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
context?: EvaluationContext;
|
|
31
|
+
pollingInterval?: number;
|
|
32
|
+
bootstrap?: {
|
|
33
|
+
flags?: Record<string, FlagValue>;
|
|
34
|
+
configs?: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
debug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
type EdgeFlagsEvent = 'ready' | 'change' | 'error';
|
|
39
|
+
type EventPayloadMap = {
|
|
40
|
+
ready: undefined;
|
|
41
|
+
change: ChangeEvent;
|
|
42
|
+
error: Error;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
declare class EdgeFlags {
|
|
46
|
+
private cache;
|
|
47
|
+
private emitter;
|
|
48
|
+
private logger;
|
|
49
|
+
private fetcher;
|
|
50
|
+
private poller;
|
|
51
|
+
private context;
|
|
52
|
+
private pollingInterval;
|
|
53
|
+
private ready;
|
|
54
|
+
private mockData;
|
|
55
|
+
constructor(options: EdgeFlagsOptions);
|
|
56
|
+
init(): Promise<void>;
|
|
57
|
+
flag(key: string): FlagValue | undefined;
|
|
58
|
+
flag<T extends FlagValue>(key: string, defaultValue: T): T;
|
|
59
|
+
config(key: string): unknown;
|
|
60
|
+
config<T>(key: string, defaultValue: T): T;
|
|
61
|
+
allFlags(): Record<string, FlagValue>;
|
|
62
|
+
allConfigs(): Record<string, unknown>;
|
|
63
|
+
identify(context: EvaluationContext): Promise<void>;
|
|
64
|
+
refresh(): Promise<void>;
|
|
65
|
+
private fetchAndSeed;
|
|
66
|
+
on<E extends EdgeFlagsEvent>(event: E, fn: (payload: EventPayloadMap[E]) => void): () => void;
|
|
67
|
+
get isReady(): boolean;
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare class EdgeFlagsError extends Error {
|
|
72
|
+
readonly statusCode: number | undefined;
|
|
73
|
+
constructor(message: string, statusCode?: number);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface MockOptions {
|
|
77
|
+
flags?: Record<string, FlagValue>;
|
|
78
|
+
configs?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
declare function createMockClient(options?: MockOptions): EdgeFlags;
|
|
81
|
+
|
|
82
|
+
export { type ChangeEvent, EdgeFlags, EdgeFlagsError, type EdgeFlagsEvent, type EdgeFlagsOptions, type EvaluationContext, type EvaluationResponse, type EventPayloadMap, type FlagValue, createMockClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var a=class extends Error{statusCode;constructor(e,t){super(e),this.name="EdgeFlagsError",this.statusCode=t;}};var g=class{enabled;constructor(e){this.enabled=e;}debug(e,...t){this.enabled&&console.debug(`[EdgeFlags] ${e}`,...t);}warn(e,...t){this.enabled&&console.warn(`[EdgeFlags] ${e}`,...t);}error(e,...t){this.enabled&&console.error(`[EdgeFlags] ${e}`,...t);}};var c=class{listeners=new Map;on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{this.listeners.get(e)?.delete(t);}}emit(e,t){let r=this.listeners.get(e);if(r)for(let s of r)s(t);}removeAll(){this.listeners.clear();}};var u=class{flags=new Map;configs=new Map;getFlag(e){return this.flags.get(e)}getConfig(e){return this.configs.get(e)}allFlags(){return Object.fromEntries(this.flags)}allConfigs(){return Object.fromEntries(this.configs)}update(e,t){let r={flags:[],configs:[]};for(let[s,i]of Object.entries(e)){let o=this.flags.get(s);f(o,i)||r.flags.push({key:s,previous:o,current:i}),this.flags.set(s,i);}for(let[s,i]of Object.entries(t)){let o=this.configs.get(s);f(o,i)||r.configs.push({key:s,previous:o,current:i}),this.configs.set(s,i);}return r.flags.length===0&&r.configs.length===0?null:r}seed(e,t){for(let[r,s]of Object.entries(e))this.flags.set(r,s);for(let[r,s]of Object.entries(t))this.configs.set(r,s);}clear(){this.flags.clear(),this.configs.clear();}};function f(n,e){if(n===e)return true;if(n===null||e===null||n===void 0||e===void 0||typeof n!=typeof e||typeof n!="object")return false;let t=Object.keys(n),r=Object.keys(e);if(t.length!==r.length)return false;for(let s of t)if(!f(n[s],e[s]))return false;return true}var d=class{baseUrl;token;constructor(e,t){this.baseUrl=e.replace(/\/$/,""),this.token=t;}async fetchAll(e){let t=await fetch(`${this.baseUrl}/api/v1/evaluate`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify({context:e})});if(!t.ok)throw new a(`Evaluation request failed: ${t.status} ${t.statusText}`,t.status);return await t.json()}};var h=class{intervalMs;timer=null;task;onError;constructor(e,t,r){this.intervalMs=e,this.task=t,this.onError=r;}start(){this.timer||(this.timer=setInterval(async()=>{try{await this.task();}catch(e){this.onError(e instanceof Error?e:new Error(String(e)));}},this.intervalMs));}stop(){this.timer&&(clearInterval(this.timer),this.timer=null);}get running(){return this.timer!==null}};var p={custom:{}},v=6e4,l=class{cache;emitter;logger;fetcher;poller=null;context;pollingInterval;ready=false;mockData;constructor(e){let t=e._mock??null;this.mockData=t,this.cache=new u,this.emitter=new c,this.logger=new g(e.debug??false),this.context=e.context??{...p},this.pollingInterval=e.pollingInterval??v,t?(this.fetcher=null,this.cache.seed(t.flags,t.configs),this.ready=true,this.logger.debug("Mock client created")):(this.fetcher=new d(e.baseUrl,e.token),e.bootstrap&&(this.cache.seed(e.bootstrap.flags??{},e.bootstrap.configs??{}),this.logger.debug("Bootstrap data loaded")));}async init(){if(this.mockData){this.emitter.emit("ready",void 0);return}try{await this.fetchAndSeed(),this.ready=!0,this.logger.debug("Initialized"),this.emitter.emit("ready",void 0),this.poller=new h(this.pollingInterval,()=>this.refresh(),e=>{this.logger.error("Polling error",e),this.emitter.emit("error",e);}),this.poller.start(),this.logger.debug(`Polling started (${this.pollingInterval}ms)`);}catch(e){let t=e instanceof Error?e:new Error(String(e));if(this.logger.error("Init failed",t),this.emitter.emit("error",t),this.cache.allFlags()&&Object.keys(this.cache.allFlags()).length>0)this.ready=true,this.logger.warn("Using bootstrap data after init failure"),this.emitter.emit("ready",void 0);else throw t}}flag(e,t){let r=this.cache.getFlag(e);return r===void 0?t:r}config(e,t){let r=this.cache.getConfig(e);return r===void 0?t:r}allFlags(){return this.cache.allFlags()}allConfigs(){return this.cache.allConfigs()}async identify(e){this.context=e,this.logger.debug("Context updated",e),this.ready&&this.fetcher&&await this.refresh();}async refresh(){if(!this.fetcher)return;this.logger.debug("Fetching evaluations");let e=await this.fetcher.fetchAll(this.context),t=this.cache.update(e.flags,e.configs);t&&(this.logger.debug("Changes detected",t),this.emitter.emit("change",t));}async fetchAndSeed(){if(!this.fetcher)return;this.logger.debug("Fetching initial evaluations");let e=await this.fetcher.fetchAll(this.context);this.cache.seed(e.flags,e.configs);}on(e,t){return this.emitter.on(e,t)}get isReady(){return this.ready}destroy(){this.poller?.stop(),this.poller=null,this.cache.clear(),this.emitter.removeAll(),this.ready=false,this.logger.debug("Destroyed");}};function E(n={}){return new l({token:"mock_token",baseUrl:"http://localhost",_mock:{flags:n.flags??{},configs:n.configs??{}}})}
|
|
2
|
+
export{l as EdgeFlags,a as EdgeFlagsError,E as createMockClient};//# sourceMappingURL=index.js.map
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/logger.ts","../src/emitter.ts","../src/cache.ts","../src/fetcher.ts","../src/poller.ts","../src/client.ts","../src/mock.ts"],"names":["EdgeFlagsError","message","statusCode","Logger","enabled","args","Emitter","event","fn","payload","fns","Cache","key","flags","configs","changes","current","previous","deepEqual","value","a","b","keysA","keysB","Fetcher","baseUrl","token","context","res","Poller","intervalMs","task","onError","err","DEFAULT_CONTEXT","DEFAULT_POLL_INTERVAL","EdgeFlags","options","mock","error","defaultValue","data","createMockClient"],"mappings":"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","file":"index.js","sourcesContent":["export class EdgeFlagsError extends Error {\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'EdgeFlagsError';\n this.statusCode = statusCode;\n }\n}\n","export class Logger {\n private enabled: boolean;\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n }\n\n debug(message: string, ...args: unknown[]): void {\n if (this.enabled) {\n console.debug(`[EdgeFlags] ${message}`, ...args);\n }\n }\n\n warn(message: string, ...args: unknown[]): void {\n if (this.enabled) {\n console.warn(`[EdgeFlags] ${message}`, ...args);\n }\n }\n\n error(message: string, ...args: unknown[]): void {\n if (this.enabled) {\n console.error(`[EdgeFlags] ${message}`, ...args);\n }\n }\n}\n","import type { EdgeFlagsEvent, EventPayloadMap } from './types.js';\n\ntype Listener<E extends EdgeFlagsEvent> = (payload: EventPayloadMap[E]) => void;\n\nexport class Emitter {\n private listeners = new Map<EdgeFlagsEvent, Set<Listener<never>>>();\n\n on<E extends EdgeFlagsEvent>(event: E, fn: Listener<E>): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(fn as Listener<never>);\n\n return () => {\n this.listeners.get(event)?.delete(fn as Listener<never>);\n };\n }\n\n emit<E extends EdgeFlagsEvent>(event: E, payload: EventPayloadMap[E]): void {\n const fns = this.listeners.get(event);\n if (!fns) return;\n for (const fn of fns) {\n (fn as Listener<E>)(payload);\n }\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n","import type { FlagValue, ChangeEvent } from './types.js';\n\nexport class Cache {\n private flags = new Map<string, FlagValue>();\n private configs = new Map<string, unknown>();\n\n getFlag(key: string): FlagValue | undefined {\n return this.flags.get(key);\n }\n\n getConfig(key: string): unknown {\n return this.configs.get(key);\n }\n\n allFlags(): Record<string, FlagValue> {\n return Object.fromEntries(this.flags);\n }\n\n allConfigs(): Record<string, unknown> {\n return Object.fromEntries(this.configs);\n }\n\n update(\n flags: Record<string, FlagValue>,\n configs: Record<string, unknown>,\n ): ChangeEvent | null {\n const changes: ChangeEvent = { flags: [], configs: [] };\n\n for (const [key, current] of Object.entries(flags)) {\n const previous = this.flags.get(key);\n if (!deepEqual(previous, current)) {\n changes.flags.push({ key, previous, current });\n }\n this.flags.set(key, current);\n }\n\n for (const [key, current] of Object.entries(configs)) {\n const previous = this.configs.get(key);\n if (!deepEqual(previous, current)) {\n changes.configs.push({ key, previous, current });\n }\n this.configs.set(key, current);\n }\n\n if (changes.flags.length === 0 && changes.configs.length === 0) {\n return null;\n }\n return changes;\n }\n\n seed(flags: Record<string, FlagValue>, configs: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(flags)) {\n this.flags.set(key, value);\n }\n for (const [key, value] of Object.entries(configs)) {\n this.configs.set(key, value);\n }\n }\n\n clear(): void {\n this.flags.clear();\n this.configs.clear();\n }\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null || a === undefined || b === undefined) return false;\n if (typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n const keysA = Object.keys(a as Record<string, unknown>);\n const keysB = Object.keys(b as Record<string, unknown>);\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false;\n }\n }\n return true;\n}\n","import type { EvaluationContext, EvaluationResponse } from './types.js';\nimport { EdgeFlagsError } from './errors.js';\n\nexport class Fetcher {\n private baseUrl: string;\n private token: string;\n\n constructor(baseUrl: string, token: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.token = token;\n }\n\n async fetchAll(context: EvaluationContext): Promise<EvaluationResponse> {\n const res = await fetch(`${this.baseUrl}/api/v1/evaluate`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ context }),\n });\n\n if (!res.ok) {\n throw new EdgeFlagsError(\n `Evaluation request failed: ${res.status} ${res.statusText}`,\n res.status,\n );\n }\n\n return (await res.json()) as EvaluationResponse;\n }\n}\n","export class Poller {\n private intervalMs: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n private task: () => Promise<void>;\n private onError: (err: Error) => void;\n\n constructor(\n intervalMs: number,\n task: () => Promise<void>,\n onError: (err: Error) => void,\n ) {\n this.intervalMs = intervalMs;\n this.task = task;\n this.onError = onError;\n }\n\n start(): void {\n if (this.timer) return;\n this.timer = setInterval(async () => {\n try {\n await this.task();\n } catch (err) {\n this.onError(err instanceof Error ? err : new Error(String(err)));\n }\n }, this.intervalMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n get running(): boolean {\n return this.timer !== null;\n }\n}\n","import type {\n FlagValue,\n EvaluationContext,\n EdgeFlagsOptions,\n EdgeFlagsEvent,\n EventPayloadMap,\n ChangeEvent,\n} from './types.js';\nimport { EdgeFlagsError } from './errors.js';\nimport { Logger } from './logger.js';\nimport { Emitter } from './emitter.js';\nimport { Cache } from './cache.js';\nimport { Fetcher } from './fetcher.js';\nimport { Poller } from './poller.js';\n\ninterface MockData {\n flags: Record<string, FlagValue>;\n configs: Record<string, unknown>;\n}\n\nconst DEFAULT_CONTEXT: EvaluationContext = { custom: {} };\nconst DEFAULT_POLL_INTERVAL = 60_000;\n\nexport class EdgeFlags {\n private cache: Cache;\n private emitter: Emitter;\n private logger: Logger;\n private fetcher: Fetcher | null;\n private poller: Poller | null = null;\n private context: EvaluationContext;\n private pollingInterval: number;\n private ready = false;\n private mockData: MockData | null;\n\n constructor(options: EdgeFlagsOptions) {\n const mock = (options as EdgeFlagsOptions & { _mock?: MockData })._mock ?? null;\n this.mockData = mock;\n this.cache = new Cache();\n this.emitter = new Emitter();\n this.logger = new Logger(options.debug ?? false);\n this.context = options.context ?? { ...DEFAULT_CONTEXT };\n this.pollingInterval = options.pollingInterval ?? DEFAULT_POLL_INTERVAL;\n\n if (mock) {\n this.fetcher = null;\n this.cache.seed(mock.flags, mock.configs);\n this.ready = true;\n this.logger.debug('Mock client created');\n } else {\n this.fetcher = new Fetcher(options.baseUrl, options.token);\n if (options.bootstrap) {\n this.cache.seed(\n options.bootstrap.flags ?? {},\n options.bootstrap.configs ?? {},\n );\n this.logger.debug('Bootstrap data loaded');\n }\n }\n }\n\n async init(): Promise<void> {\n if (this.mockData) {\n this.emitter.emit('ready', undefined);\n return;\n }\n\n try {\n await this.fetchAndSeed();\n this.ready = true;\n this.logger.debug('Initialized');\n this.emitter.emit('ready', undefined);\n\n this.poller = new Poller(\n this.pollingInterval,\n () => this.refresh(),\n (err) => {\n this.logger.error('Polling error', err);\n this.emitter.emit('error', err);\n },\n );\n this.poller.start();\n this.logger.debug(`Polling started (${this.pollingInterval}ms)`);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.logger.error('Init failed', error);\n this.emitter.emit('error', error);\n\n if (this.cache.allFlags() && Object.keys(this.cache.allFlags()).length > 0) {\n this.ready = true;\n this.logger.warn('Using bootstrap data after init failure');\n this.emitter.emit('ready', undefined);\n } else {\n throw error;\n }\n }\n }\n\n flag(key: string): FlagValue | undefined;\n flag<T extends FlagValue>(key: string, defaultValue: T): T;\n flag(key: string, defaultValue?: FlagValue): FlagValue | undefined {\n const value = this.cache.getFlag(key);\n if (value === undefined) return defaultValue;\n return value;\n }\n\n config(key: string): unknown;\n config<T>(key: string, defaultValue: T): T;\n config(key: string, defaultValue?: unknown): unknown {\n const value = this.cache.getConfig(key);\n if (value === undefined) return defaultValue;\n return value;\n }\n\n allFlags(): Record<string, FlagValue> {\n return this.cache.allFlags();\n }\n\n allConfigs(): Record<string, unknown> {\n return this.cache.allConfigs();\n }\n\n async identify(context: EvaluationContext): Promise<void> {\n this.context = context;\n this.logger.debug('Context updated', context);\n if (this.ready && this.fetcher) {\n await this.refresh();\n }\n }\n\n async refresh(): Promise<void> {\n if (!this.fetcher) return;\n\n this.logger.debug('Fetching evaluations');\n const data = await this.fetcher.fetchAll(this.context);\n const changes = this.cache.update(data.flags, data.configs);\n\n if (changes) {\n this.logger.debug('Changes detected', changes);\n this.emitter.emit('change', changes);\n }\n }\n\n private async fetchAndSeed(): Promise<void> {\n if (!this.fetcher) return;\n\n this.logger.debug('Fetching initial evaluations');\n const data = await this.fetcher.fetchAll(this.context);\n this.cache.seed(data.flags, data.configs);\n }\n\n on<E extends EdgeFlagsEvent>(\n event: E,\n fn: (payload: EventPayloadMap[E]) => void,\n ): () => void {\n return this.emitter.on(event, fn);\n }\n\n get isReady(): boolean {\n return this.ready;\n }\n\n destroy(): void {\n this.poller?.stop();\n this.poller = null;\n this.cache.clear();\n this.emitter.removeAll();\n this.ready = false;\n this.logger.debug('Destroyed');\n }\n}\n","import type { FlagValue, EdgeFlagsOptions } from './types.js';\nimport { EdgeFlags } from './client.js';\n\nexport interface MockOptions {\n flags?: Record<string, FlagValue>;\n configs?: Record<string, unknown>;\n}\n\nexport function createMockClient(options: MockOptions = {}): EdgeFlags {\n const client = new EdgeFlags({\n token: 'mock_token',\n baseUrl: 'http://localhost',\n _mock: {\n flags: options.flags ?? {},\n configs: options.configs ?? {},\n },\n } as EdgeFlagsOptions & { _mock: { flags: Record<string, FlagValue>; configs: Record<string, unknown> } });\n\n return client;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edgeflags/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript/TypeScript SDK for EdgeFlags feature flag service",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsup": "^8.0.0",
|
|
26
|
+
"typescript": "^5.7.0"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"feature-flags",
|
|
30
|
+
"edgeflags",
|
|
31
|
+
"sdk"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/edgeflags/sdks.git",
|
|
37
|
+
"directory": "packages/sdk"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://docs.edgeflags.net/guides/client-sdks/",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/edgeflags/sdks/issues"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"clean": "rm -rf dist"
|
|
47
|
+
}
|
|
48
|
+
}
|