@configdirector/server-sdk 0.1.0 → 0.2.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/configdirector-server.cjs +1 -1
- package/dist/configdirector-server.d.cts +29 -0
- package/dist/configdirector-server.d.cts.map +1 -1
- package/dist/configdirector-server.d.mts +29 -0
- package/dist/configdirector-server.d.mts.map +1 -1
- package/dist/configdirector-server.mjs +1 -1
- package/dist/configdirector-server.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`eventsource-client`),l=require(`node:events`);l=s(l);let u=require(`@jsonjoy.com/json-pointer`),d=require(`semver`);d=s(d);let f=require(`rapidhash-js`);var p=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},m=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}},h=class{constructor(e){this.options=e,this.eventEmitter=new l.EventEmitter,this.options=e,this.logger=e.logger,this.url=new URL(`server/sse/v1`,e.baseUrl)}async connect(e){this.eventSource&&this.close();let t,n,r=async(e,r)=>{let i=await fetch(e,r);return t=i.status,i.ok||(n=await i.text()),i},i=new Promise((e,i)=>{this.eventSource=(0,c.createEventSource)({url:this.url,fetch:r,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({metaContext:this.options.metaContext,serverSdkKey:this.options.serverSdkKey}),onMessage:({data:e})=>{this.dispatchMessage(e)},onConnect:()=>{t&&this.isStatusFatal(t)?(this.close(),i(this.prepareFatalError(t,n))):(this.logger.debug(`[EventSourceTransport] Connected, status: %s`,t),e(this))},onScheduleReconnect:e=>{t&&this.isStatusFatal(t)?(this.close(),i(this.prepareFatalError(t,n))):this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${e.delay}. Response status: ${t}`)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}})});return Promise.race([i,new Promise(t=>{setTimeout(()=>t(this),e)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configBundleReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,t){let n=e??0;return new p(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}isStatusFatal(e){return!!e&&e>=400&&e<500}get isConnected(){return this.eventSource?.readyState==`open`}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}clear(){this.eventEmitter.removeAllListeners()}close(){this.eventSource?.close()}dispose(){this.close(),this.clear()}};const g=(e,t,n)=>{switch(t.toLowerCase()){case`=`:case`equals`:return e==n[0];case`!=`:case`does not equal`:return e!=n[0];case`is one of`:return n.includes(e);case`is not one of`:return!n.includes(e);case`starts with any of`:return n.findIndex(t=>e?.startsWith(t))>=0;case`does not start with any of`:return n.findIndex(t=>e?.startsWith(t))<0;case`ends with any of`:return n.findIndex(t=>e?.endsWith(t))>=0;case`does not end with any of`:return n.findIndex(t=>e?.endsWith(t))<0;case`matches regex`:return _(n[0],e);case`does not match regex`:return!_(n[0],e);default:return!1}},_=(e,t)=>{try{return new RegExp(e).test(t)}catch{return!1}},v=(e,t,n)=>{if(typeof e==`boolean`)return!1;let r=t.toLowerCase();if(e==null)return r==`!=`||r==`does not equal`;let{value:i,target:a}=y(e,n[0]);switch(r){case`=`:case`equals`:return i==a;case`!=`:case`does not equal`:return i!=a;case`<`:return i<a;case`<=`:return i<=a;case`>`:return i>a;case`>=`:return i>=a;default:return!1}},y=(e,t)=>typeof e==`number`?{value:e,target:b(t)}:typeof e==`bigint`?{value:e,target:BigInt(t)}:{value:b(e),target:b(t)},b=e=>{let t=Number.parseFloat(e.toString());if(isNaN(t)||!isFinite(t))throw TypeError(`Invalid numeric value: ${e}`);return t},x=(e,t,n)=>{if(e==null)return!1;try{let r=new Date(e),i=new Date(n[0]);switch(t){case`is after`:return r>i;case`is before`:return r<i;default:return!1}}catch{return!1}},S=(e,t,n)=>{let r=t.toLowerCase();if(e==null||e.trim().length==0)return r==`is not one of`;let i=d.coerce(e)??e,a=n.map(e=>d.coerce(e)??e);try{switch(r){case`=`:return C(i,a[0]);case`<`:return d.lt(i,a[0]);case`<=`:return d.lte(i,a[0]);case`>`:return d.gt(i,a[0]);case`>=`:return d.gte(i,a[0]);case`is one of`:return a.findIndex(e=>C(i,e))>=0;case`is not one of`:return a.findIndex(e=>C(i,e))<0;default:return!1}}catch{return!1}},C=(e,t)=>{try{return d.eq(e,t)}catch{return!1}},w=(e,t,n)=>{let r=t.toLowerCase();if(!Array.isArray(e))return r==`does not contain any of`;switch(r){case`contains any of`:return e.some(e=>n.includes(e));case`does not contain any of`:return!e.some(e=>n.includes(e));default:return!1}};var T=class{evaluate(e,t){if(!this.isConditionValidForComparison(e))return!1;switch(e.attribute){case`identifier`:return this.compare(t?.context?.id,e);case`name`:return this.compare(t?.context?.name,e);case`appName`:return this.compare(t?.metadata?.appName,e);case`appVersion`:return this.compare(t?.metadata?.appVersion,e);case`traits`:if(e.trait){let n=this.findTraitValue(t?.context?.traits,e.trait);return this.compare(n,e)}}return!1}compare(e,t){switch(t.targetType){case`text`:return g(e?.toString(),t.operator,t.targetValues);case`number`:return v(e,t.operator,t.targetValues);case`datetime`:return x(e?.toString(),t.operator,t.targetValues);case`semver`:return S(e?.toString(),t.operator,t.targetValues);case`array`:return w(e,t.operator,t.targetValues)}return!1}findTraitValue(e,t){try{return(0,u.findByPointer)(t,e).val}catch{return}}isConditionValidForComparison(e){return Array.isArray(e.targetValues)&&e.targetValues.length>0}};const E=e=>{let t=`${e.contextIdentifier}-${e.configId}`;return Number((0,f.rapidhash)(t,{seed:15426857351n})%1000n)/10};var D=class{constructor(e){if(this.logger=e,this.conditionEvaluator=new T,!e||!e.warn||!e.error||!e.info)throw TypeError(`The provided logger is not a valid 'ConfigDirectorLogger'`);this.logger=e}evaluate(e,t){return{id:e.id,key:e.key,type:e.type,value:this.getConfigValue(e,t)}}getConfigValue(e,t){let n=[...e.target?.rules??[]].sort((e,t)=>(e.order??2**53-1)-(t.order??2**53-1));for(let r of n){let n=this.evaluateRule(r,e,t);if(n.success)return n.value}return e.target?.defaultValue}evaluateRule(e,t,n){try{if(e.type==`percentage`)return this.evaluatePercentageRule(e,t,n);if(e.type==`conditional`)return this.evaluateConditionalRule(e,t,n)}catch(n){this.logger.warn(`There was an error while evaluating a targeting rule '${e?.id}' for '${t?.key}'. The rule will be disregarded.`,{error:n,configKey:t?.key,ruleId:e?.id})}return{success:!1}}evaluatePercentageRule(e,t,n){return this.evaluatePercentage(e.percentages??[],t,n)}evaluatePercentage(e,t,n){let r=E({configId:t.id,contextIdentifier:n?.context?.id??crypto.randomUUID()}),i=0,a=0,o;for(;!o&&i<e.length;)r<=e[i].percentage+a&&(o=e[i]),a+=e[i].percentage,i+=1;return o?.value==null?{success:!1}:{success:!0,value:o.value.toString()}}evaluateConditionalRule(e,t,n){let r=e.conditions??[],i=0,a;for(;!a&&i<r.length;)this.conditionEvaluator.evaluate(r[i],n)&&(a=r[i]),i+=1;return a&&e.target==`value`&&e.value!=null?{success:!0,value:e.value.toString()}:a&&e.target==`percentage`?this.evaluatePercentage(e.percentages??[],t,n):{success:!1}}};const O=e=>{let t=typeof e;if(t===`object`)try{return e.constructor?.name??t}catch{return t}else if(t===`function`)try{let n=e.name;return n?`function: ${n}`:t}catch{return t}return t},k=e=>e===`number`||e===`bigint`,A=(e,t)=>{let n=e.value,r=O(t);if(!n)return{parsedValue:t,requestedType:r,usedDefault:!0,reason:`value-missing`};if(typeof t==`string`)return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`};if(typeof t==`boolean`&&e.type===`boolean`){let e=j(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(k(typeof t)&&e.type===`integer`){let e=M(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(k(typeof t)&&(e.type===`float`||e.type===`enum`)){let e=N(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`}},j=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},M=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},N=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},P={off:-1,error:0,warn:1,info:2,debug:3},F=()=>{let e={year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,timeZoneName:`short`};try{return new Intl.DateTimeFormat(void 0,{...e,fractionalSecondDigits:3})}catch{return new Intl.DateTimeFormat(void 0,e)}};var I=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=F()}debug(e,...t){this.log(console.debug,`debug`,e,...t)}info(e,...t){this.log(console.info,`info`,e,...t)}warn(e,...t){this.log(console.warn,`warn`,e,...t)}error(e,...t){this.log(console.error,`error`,e,...t)}log(e,t,n,...r){P[this.level]>=P[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},L=class{decorateMessage(e){return`[ConfigDirector:js-server-sdk] ${e}`}};const R=(e,t)=>new I(e??`warn`,t??new L),z=e=>B(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),B=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},V=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},H=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},U=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(t,{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var W=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`server/telemetry/v1`,e.baseUrl)}async report({discreteEvents:e,aggregatedEvents:t,droppedEvents:n}){if(!this.executeRequests)return{success:!1,fatalError:!0};let r={serverSdkKey:this.sdkKey,discreteEvents:e,aggregatedEvents:t,droppedEvents:n};if(this.isReportEmpty(r))return{success:!0,fatalError:!1};let i=await this.sendReport(r);return i.fatalError&&(this.executeRequests=!1),i}isReportEmpty(e){return V(e.discreteEvents)&&V(e.aggregatedEvents)&&H(e.droppedEvents)}async sendReport(e){try{let t=await U(5e3,this.url,{method:`POST`,body:JSON.stringify(e)},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:!1}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},G=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},K=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},q=class{constructor(e){this.evaluationEventQueue=new K,this.aggregator=new G,this.collectEvents=!0,this.logger=e.logger,this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(e,t){if(t===`json`)try{return z(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot();return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},J=class extends q{constructor(e){super(e),this.reporter=new W(e)}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(e))}sanitizeEvaluatedConfigEvent(e){return{key:e.key,type:e.type,defaultValue:this.sanitizeValue(e.defaultValue,e.type),requestedType:e.requestedType,evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type),usedDefault:e.usedDefault,evaluationReason:e.evaluationReason}}};const Y=new URL(`https://server-sdk-api.configdirector.com`);var X=class{constructor(e,t,n){this.handlersMap=new Map,this.eventEmitter=new l.default,this.ready=!1,this.logger=n?.logger??R(),this.timeout=n?.connection?.timeout??3e3,this.metaContext=n?.metadata??{},this.configEvaluator=new D(this.logger);let r=this.parseUrl(n?.connection?.url)??Y;this.streaming=n?.connection?.streaming!==!1;let i=h;this.usageEventCollector=new J({sdkKey:e,logger:this.logger,baseUrl:r}),this.transport=new i({serverSdkKey:e,baseUrl:r,metaContext:{...n?.metadata,sdkName:t.sdkName,sdkVersion:t.sdkVersion},logger:this.logger}),this.transport.on(`configBundleReceived`,e=>{this.readyResolve?.();let t=Object.keys(e.configs);!this.configSet||e.kind==`full`?(this.configSet=e,this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)):(this.configSet.configs={...this.configSet.configs,...e.configs},this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)),this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server:`,{keys:t})})}async initialize(){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.eventEmitter.emit(`clientReady`),this.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let e=new Date().getTime();await this.transport.connect(this.timeout);let t=new Date().getTime()-e,n=this.timeout-t;if(n>0&&await Promise.race([this.readyPromise,new Promise(e=>{setTimeout(()=>e(),n)})]),!this.ready){let e=this.streaming?`The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`:`Since the client was configured without streaming, configs may not update and always return the default value.`;this.logger.warn(`[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${e}`)}}catch(e){this.logger.error(`[ConfigDirectorClient] An error occurred during initialization: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){this.handlersMap.get(e.key)?.forEach(t=>{let n=this.getValueFromConfigDefinition(e.key,e,t.defaultValue,t.context);t.handler(n)})}watch(e,t,n,r){this.validateDefaultValue(t);let i=this.handlersMap.get(e),a={handler:n,defaultValue:t,requestedType:typeof t,context:r};return i?i.push(a):this.handlersMap.set(e,[a]),()=>this.unwatch(e,n)}unwatch(e,t){let n=this.handlersMap.get(e);if(n)if(t){let e=n.findIndex(e=>e.handler==t);e>=0&&n?.splice(e,1)}else n.splice(0)}getValue(e,t,n){this.validateDefaultValue(t);let r=this.configSet?.configs[e];return this.getValueFromConfigDefinition(e,r,t,n)}getValueFromConfigDefinition(e,t,n,r){if(t){let i=A(this.configEvaluator.evaluate(t,{context:r,metadata:this.metaContext}),n);return this.usageEventCollector.evaluatedConfig({contextId:r?.id,key:e,defaultValue:n,requestedType:i.requestedType,evaluatedValue:i.parsedValue,usedDefault:i.usedDefault,evaluationReason:i.reason}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${i.parsedValue}'`),i.parsedValue}else return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({contextId:r?.id,key:e,defaultValue:n,requestedType:O(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new m(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new m(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new m(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}removeAllObservers(){this.logger.debug(`[ConfigDirectorClient] removeAllObservers() has been called, removing all observers`),this.eventEmitter.removeAllListeners(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}closeConnection(){this.logger.debug(`[ConfigDirectorClient] closeConnection() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.removeAllObservers(),this.closeConnection()}};const Z=(e,t)=>new X(e,{sdkName:`js-server-sdk`,sdkVersion:`0.1.0`},t),Q=(e,t)=>R(e,t);exports.createClient=Z,exports.createConsoleLogger=Q;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`eventsource-client`),l=require(`node:events`);l=s(l);let u=require(`@jsonjoy.com/json-pointer`),d=require(`semver`);d=s(d);let f=require(`rapidhash-js`);var p=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},m=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}},h=class{constructor(e){this.options=e,this.eventEmitter=new l.EventEmitter,this.options=e,this.logger=e.logger,this.url=new URL(`server/sse/v1`,e.baseUrl)}async connect(e){this.eventSource&&this.close();let t,n,r=async(e,r)=>{let i=await fetch(e,r);return t=i.status,i.ok||(n=await i.text()),i},i=new Promise((e,i)=>{this.eventSource=(0,c.createEventSource)({url:this.url,fetch:r,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({metaContext:this.options.metaContext,serverSdkKey:this.options.serverSdkKey}),onMessage:({data:e})=>{this.dispatchMessage(e)},onConnect:()=>{t&&this.isStatusFatal(t)?(this.close(),i(this.prepareFatalError(t,n))):(this.logger.debug(`[EventSourceTransport] Connected, status: ${t}`),e(this))},onScheduleReconnect:e=>{t&&this.isStatusFatal(t)?(this.close(),i(this.prepareFatalError(t,n))):this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${e.delay}. Response status: ${t}`)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}})});return Promise.race([i,new Promise(t=>{setTimeout(()=>t(this),e)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configBundleReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,t){let n=e??0;return new p(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}isStatusFatal(e){return!!e&&e>=400&&e<500}get isConnected(){return this.eventSource?.readyState==`open`}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}clear(){this.eventEmitter.removeAllListeners()}close(){this.eventSource?.close()}dispose(){this.close(),this.clear()}};const g=(e,t,n)=>{switch(t.toLowerCase()){case`=`:case`equals`:return e==n[0];case`!=`:case`does not equal`:return e!=n[0];case`is one of`:return n.includes(e);case`is not one of`:return!n.includes(e);case`starts with any of`:return n.findIndex(t=>e?.startsWith(t))>=0;case`does not start with any of`:return n.findIndex(t=>e?.startsWith(t))<0;case`ends with any of`:return n.findIndex(t=>e?.endsWith(t))>=0;case`does not end with any of`:return n.findIndex(t=>e?.endsWith(t))<0;case`matches regex`:return _(n[0],e);case`does not match regex`:return!_(n[0],e);default:return!1}},_=(e,t)=>{try{return new RegExp(e).test(t)}catch{return!1}},v=(e,t,n)=>{if(typeof e==`boolean`)return!1;let r=t.toLowerCase();if(e==null)return r==`!=`||r==`does not equal`;let{value:i,target:a}=y(e,n[0]);switch(r){case`=`:case`equals`:return i==a;case`!=`:case`does not equal`:return i!=a;case`<`:return i<a;case`<=`:return i<=a;case`>`:return i>a;case`>=`:return i>=a;default:return!1}},y=(e,t)=>typeof e==`number`?{value:e,target:b(t)}:typeof e==`bigint`?{value:e,target:BigInt(t)}:{value:b(e),target:b(t)},b=e=>{let t=Number.parseFloat(e.toString());if(isNaN(t)||!isFinite(t))throw TypeError(`Invalid numeric value: ${e}`);return t},x=(e,t,n)=>{if(e==null)return!1;try{let r=new Date(e),i=new Date(n[0]);switch(t){case`is after`:return r>i;case`is before`:return r<i;default:return!1}}catch{return!1}},S=(e,t,n)=>{let r=t.toLowerCase();if(e==null||e.trim().length==0)return r==`is not one of`;let i=d.coerce(e)??e,a=n.map(e=>d.coerce(e)??e);try{switch(r){case`=`:return C(i,a[0]);case`<`:return d.lt(i,a[0]);case`<=`:return d.lte(i,a[0]);case`>`:return d.gt(i,a[0]);case`>=`:return d.gte(i,a[0]);case`is one of`:return a.findIndex(e=>C(i,e))>=0;case`is not one of`:return a.findIndex(e=>C(i,e))<0;default:return!1}}catch{return!1}},C=(e,t)=>{try{return d.eq(e,t)}catch{return!1}},w=(e,t,n)=>{let r=t.toLowerCase();if(!Array.isArray(e))return r==`does not contain any of`;switch(r){case`contains any of`:return e.some(e=>n.includes(e));case`does not contain any of`:return!e.some(e=>n.includes(e));default:return!1}};var T=class{evaluate(e,t){if(!this.isConditionValidForComparison(e))return!1;switch(e.attribute){case`identifier`:return this.compare(t?.context?.id,e);case`name`:return this.compare(t?.context?.name,e);case`appName`:return this.compare(t?.metadata?.appName,e);case`appVersion`:return this.compare(t?.metadata?.appVersion,e);case`traits`:if(e.trait){let n=this.findTraitValue(t?.context?.traits,e.trait);return this.compare(n,e)}}return!1}compare(e,t){switch(t.targetType){case`text`:return g(e?.toString(),t.operator,t.targetValues);case`number`:return v(e,t.operator,t.targetValues);case`datetime`:return x(e?.toString(),t.operator,t.targetValues);case`semver`:return S(e?.toString(),t.operator,t.targetValues);case`array`:return w(e,t.operator,t.targetValues)}return!1}findTraitValue(e,t){try{return(0,u.findByPointer)(t,e).val}catch{return}}isConditionValidForComparison(e){return Array.isArray(e.targetValues)&&e.targetValues.length>0}};const E=e=>{let t=`${e.contextIdentifier}-${e.configId}`;return Number((0,f.rapidhash)(t,{seed:15426857351n})%1000n)/10};var D=class{constructor(e){if(this.logger=e,this.conditionEvaluator=new T,!e||!e.warn||!e.error||!e.info)throw TypeError(`The provided logger is not a valid 'ConfigDirectorLogger'`);this.logger=e}evaluate(e,t){return{id:e.id,key:e.key,type:e.type,value:this.getConfigValue(e,t)}}getConfigValue(e,t){let n=[...e.target?.rules??[]].sort((e,t)=>(e.order??2**53-1)-(t.order??2**53-1));for(let r of n){let n=this.evaluateRule(r,e,t);if(n.success)return n.value}return e.target?.defaultValue}evaluateRule(e,t,n){try{if(e.type==`percentage`)return this.evaluatePercentageRule(e,t,n);if(e.type==`conditional`)return this.evaluateConditionalRule(e,t,n)}catch(n){this.logger.warn(`There was an error while evaluating a targeting rule '${e?.id}' for '${t?.key}'. The rule will be disregarded.`,{error:n,configKey:t?.key,ruleId:e?.id})}return{success:!1}}evaluatePercentageRule(e,t,n){return this.evaluatePercentage(e.percentages??[],t,n)}evaluatePercentage(e,t,n){let r=E({configId:t.id,contextIdentifier:n?.context?.id??crypto.randomUUID()}),i=0,a=0,o;for(;!o&&i<e.length;)r<=e[i].percentage+a&&(o=e[i]),a+=e[i].percentage,i+=1;return o?.value==null?{success:!1}:{success:!0,value:o.value.toString()}}evaluateConditionalRule(e,t,n){let r=e.conditions??[],i=0,a;for(;!a&&i<r.length;)this.conditionEvaluator.evaluate(r[i],n)&&(a=r[i]),i+=1;return a&&e.target==`value`&&e.value!=null?{success:!0,value:e.value.toString()}:a&&e.target==`percentage`?this.evaluatePercentage(e.percentages??[],t,n):{success:!1}}};const O=e=>{let t=typeof e;if(t===`object`)try{return e.constructor?.name??t}catch{return t}else if(t===`function`)try{let n=e.name;return n?`function: ${n}`:t}catch{return t}return t},k=e=>e===`number`||e===`bigint`,A=(e,t)=>{let n=e.value,r=O(t);if(!n)return{parsedValue:t,requestedType:r,usedDefault:!0,reason:`value-missing`};if(typeof t==`string`)return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`};if(typeof t==`boolean`&&e.type===`boolean`){let e=j(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(k(typeof t)&&e.type===`integer`){let e=M(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(k(typeof t)&&(e.type===`float`||e.type===`enum`)){let e=N(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`}},j=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},M=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},N=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},P={off:-1,error:0,warn:1,info:2,debug:3},F=()=>{let e={year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,timeZoneName:`short`};try{return new Intl.DateTimeFormat(void 0,{...e,fractionalSecondDigits:3})}catch{return new Intl.DateTimeFormat(void 0,e)}};var I=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=F()}debug(e,...t){this.log(console.debug,`debug`,e,...t)}info(e,...t){this.log(console.info,`info`,e,...t)}warn(e,...t){this.log(console.warn,`warn`,e,...t)}error(e,...t){this.log(console.error,`error`,e,...t)}log(e,t,n,...r){P[this.level]>=P[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},L=class{decorateMessage(e){return`[ConfigDirector:js-server-sdk] ${e}`}};const R=(e,t)=>new I(e??`warn`,t??new L),z=e=>B(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),B=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},V=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},H=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},U=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(t,{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var W=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`server/telemetry/v1`,e.baseUrl)}async report({discreteEvents:e,aggregatedEvents:t,droppedEvents:n}){if(!this.executeRequests)return{success:!1,fatalError:!0};let r={serverSdkKey:this.sdkKey,discreteEvents:e,aggregatedEvents:t,droppedEvents:n};if(this.isReportEmpty(r))return{success:!0,fatalError:!1};let i=await this.sendReport(r);return i.fatalError&&(this.executeRequests=!1),i}isReportEmpty(e){return V(e.discreteEvents)&&V(e.aggregatedEvents)&&H(e.droppedEvents)}async sendReport(e){try{let t=await U(5e3,this.url,{method:`POST`,body:JSON.stringify(e)},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:!1}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},G=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},K=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||=new Date;let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},q=class{constructor(e){this.aggregator=new G,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new K(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(e,t){if(t===`json`)try{return z(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot();return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},J=class extends Map{constructor(e,t){super(t),this._droppedCount=0,this.limit=e,this.enforceLimit()}set(e,t){return super.set(e,t),this.enforceLimit(),this}get droppedCount(){return this._droppedCount}clearAndReset(){this.clear(),this._droppedCount=0}enforceLimit(){for(;this.size>this.limit;){let e=this.keys().next().value;e!==void 0&&(this.delete(e),this._droppedCount+=1)}}},Y=class extends q{constructor(e){super(e),this.reporter=new W(e),this.contexts=new J(e.contextLimit??1e3)}evaluatedConfig(e){if(!this.collectEvents)return;e.context?.id&&this.contexts.set(e.context.id,e.context);let t={...e.evaluation,contextId:e.context?.id};this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(t))}sanitizeEvaluatedConfigEvent(e){return{...e,defaultValue:this.sanitizeValue(e.defaultValue,e.type),evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type)}}snapshotContexts(){let e=Array.from(this.contexts.values()),t=this.contexts.droppedCount;return this.contexts.clearAndReset(),{contexts:e,droppedCount:t}}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot(),t=this.snapshotContexts();return await this.reporter?.report({context:this._context,discreteEvents:{capturedContexts:t.contexts},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount,capturedContexts:t.droppedCount}})}};const X=new URL(`https://server-sdk-api.configdirector.com`);var Z=class{constructor(e,t,n){this.handlersMap=new Map,this.eventEmitter=new l.default,this.ready=!1,this.logger=n?.logger??R(),this.timeout=n?.connection?.timeout??3e3,this.metaContext=n?.metadata??{},this.configEvaluator=new D(this.logger);let r=this.parseUrl(n?.connection?.url)??X;this.streaming=n?.connection?.streaming!==!1;let i=h,a=n?.telemetry?.eventQueueLimit??5e3;this.usageEventCollector=new Y({sdkKey:e,logger:this.logger,baseUrl:r,flushIntervalDelay:n?.telemetry?.flushInterval??3e4,evaluationQueueLimit:Math.ceil(a*.7),contextLimit:Math.floor(a*.3)}),this.transport=new i({serverSdkKey:e,baseUrl:r,metaContext:{...n?.metadata,sdkName:t.sdkName,sdkVersion:t.sdkVersion},logger:this.logger}),this.transport.on(`configBundleReceived`,e=>{this.readyResolve?.();let t=Object.keys(e.configs);!this.configSet||e.kind==`full`?(this.configSet=e,this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)):(this.configSet.configs={...this.configSet.configs,...e.configs},this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)),this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server with ${t.length} key(s)`,{keys:t})})}async initialize(){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.eventEmitter.emit(`clientReady`),this.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let e=new Date().getTime();await this.transport.connect(this.timeout);let t=new Date().getTime()-e,n=this.timeout-t;if(n>0&&await Promise.race([this.readyPromise,new Promise(e=>{setTimeout(()=>e(),n)})]),!this.ready){let e=this.streaming?`The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`:`Since the client was configured without streaming, configs may not update and always return the default value.`;this.logger.warn(`[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${e}`)}}catch(e){this.logger.error(`[ConfigDirectorClient] An error occurred during initialization: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){this.handlersMap.get(e.key)?.forEach(t=>{let n=this.getValueFromConfigDefinition(e.key,e,t.defaultValue,t.context);t.handler(n)})}watch(e,t,n,r){this.validateDefaultValue(t);let i=this.handlersMap.get(e),a={handler:n,defaultValue:t,requestedType:typeof t,context:r};return i?i.push(a):this.handlersMap.set(e,[a]),()=>this.unwatch(e,n)}unwatch(e,t){let n=this.handlersMap.get(e);if(n)if(t){let e=n.findIndex(e=>e.handler==t);e>=0&&n?.splice(e,1)}else n.splice(0)}getValue(e,t,n){this.validateDefaultValue(t);let r=this.configSet?.configs[e];return this.getValueFromConfigDefinition(e,r,t,n)}getValueFromConfigDefinition(e,t,n,r){if(t){let i=A(this.configEvaluator.evaluate(t,{context:r,metadata:this.metaContext}),n);return this.usageEventCollector.evaluatedConfig({context:r,evaluation:{key:e,defaultValue:n,requestedType:i.requestedType,evaluatedValue:i.parsedValue,usedDefault:i.usedDefault,evaluationReason:i.reason}}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${i.parsedValue}'`),i.parsedValue}else return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({context:r,evaluation:{key:e,defaultValue:n,requestedType:O(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}}),n}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new m(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new m(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new m(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}removeAllObservers(){this.logger.debug(`[ConfigDirectorClient] removeAllObservers() has been called, removing all observers`),this.eventEmitter.removeAllListeners(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}closeConnection(){this.logger.debug(`[ConfigDirectorClient] closeConnection() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.removeAllObservers(),this.closeConnection()}};const Q=(e,t)=>new Z(e,{sdkName:`js-server-sdk`,sdkVersion:`0.2.0`},t),$=(e,t)=>R(e,t);exports.createClient=Q,exports.createConsoleLogger=$;
|
|
@@ -74,6 +74,8 @@ type ConfigDirectorClientOptions = {
|
|
|
74
74
|
* If streaming is enabled, the operation (initialization or context update) may still succeed
|
|
75
75
|
* after it times out if no unrecoverable errors are encountered (like an invalid SDK key).
|
|
76
76
|
* If streaming is disabled, if the operation times out, it will not be retried.
|
|
77
|
+
*
|
|
78
|
+
* Defaults to 3,000 milliseconds (3 seconds)
|
|
77
79
|
*/
|
|
78
80
|
timeout?: number;
|
|
79
81
|
/**
|
|
@@ -97,6 +99,33 @@ type ConfigDirectorClientOptions = {
|
|
|
97
99
|
* );
|
|
98
100
|
*/
|
|
99
101
|
logger?: ConfigDirectorLogger;
|
|
102
|
+
/**
|
|
103
|
+
* Telemetry tunning. It is unlikely these settings need to be adjusted. However, in cases where
|
|
104
|
+
* your application has a large number of evaluations per second, you can adjust these settings
|
|
105
|
+
* to tune the memory footprint and frequency of telemetry requests.
|
|
106
|
+
* Keep in mind that ConfigDirector relies on these telemetry events to provide insights and features
|
|
107
|
+
* related to the configs being used.
|
|
108
|
+
*/
|
|
109
|
+
telemetry?: {
|
|
110
|
+
/**
|
|
111
|
+
* The size limit of telemetry event queues. If the size limit is reached before the events are
|
|
112
|
+
* flushed to the network, events will be dropped. ConfigDirector keeps a count of dropped events
|
|
113
|
+
* if the number of dropped events is higher than 50% of the total events, ConfigDirector will
|
|
114
|
+
* issue a notification alert in the dashboard.
|
|
115
|
+
*
|
|
116
|
+
* A number between 100 and 100,000. Defaults to 5,000.
|
|
117
|
+
*/
|
|
118
|
+
eventQueueLimit?: number;
|
|
119
|
+
/**
|
|
120
|
+
* How often events are flushed and sent over the network in milliseconds.
|
|
121
|
+
*
|
|
122
|
+
* Decrease this number if your application consistently captures a large number of events in short
|
|
123
|
+
* periods of time in order to reduce memory footprint from a large event queue.
|
|
124
|
+
*
|
|
125
|
+
* Defaults to 30,000 milliseconds (30 seconds)
|
|
126
|
+
*/
|
|
127
|
+
flushInterval?: number;
|
|
128
|
+
};
|
|
100
129
|
};
|
|
101
130
|
type ClientEvents = {
|
|
102
131
|
configsUpdated: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configdirector-server.d.cts","names":[],"sources":["../../shared/src/types.ts","../src/types.ts","../../shared/src/logger.ts","../src/api.ts"],"mappings":";KAYY,kBAAA;EAAA,CAAwB,GAAA;AAAA;AAAA,KAExB,eAAA,wCAAuD,kBAAA;AAAA,KAcvD,0BAAA;AAAA,UAEK,oBAAA;EACf,KAAA,CAAM,OAAA,aAAoB,IAAA;EAE1B,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,KAAA,CAAM,OAAA,aAAoB,IAAA;AAAA;AAAA,UAGX,iCAAA;EACf,eAAA,CAAgB,OAAA;AAAA;;;;;KAgBN,qBAAA;EApBoB;;AAGhC;;;;;AAiBA;EASE,EAAA;;;;;EAMA,IAAA;EAMY;;;AAGd;EAHE,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA;AAAA,KAGF,yBAAA;EACV,UAAA;EACA,OAAA;AAAA;;;;;;KC9DU,2BAAA;EDPe;;;ECWzB,QAAA,GAAW,yBAAA;EDGD;;;ECCV,UAAA;IDDoC;AAEtC;;;;;;;;;ICUI,SAAA;IDLF
|
|
1
|
+
{"version":3,"file":"configdirector-server.d.cts","names":[],"sources":["../../shared/src/types.ts","../src/types.ts","../../shared/src/logger.ts","../src/api.ts"],"mappings":";KAYY,kBAAA;EAAA,CAAwB,GAAA;AAAA;AAAA,KAExB,eAAA,wCAAuD,kBAAA;AAAA,KAcvD,0BAAA;AAAA,UAEK,oBAAA;EACf,KAAA,CAAM,OAAA,aAAoB,IAAA;EAE1B,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,KAAA,CAAM,OAAA,aAAoB,IAAA;AAAA;AAAA,UAGX,iCAAA;EACf,eAAA,CAAgB,OAAA;AAAA;;;;;KAgBN,qBAAA;EApBoB;;AAGhC;;;;;AAiBA;EASE,EAAA;;;;;EAMA,IAAA;EAMY;;;AAGd;EAHE,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA;AAAA,KAGF,yBAAA;EACV,UAAA;EACA,OAAA;AAAA;;;;;;KC9DU,2BAAA;EDPe;;;ECWzB,QAAA,GAAW,yBAAA;EDGD;;;ECCV,UAAA;IDDoC;AAEtC;;;;;;;;;ICUI,SAAA;IDLF;;;;;;;;ICcE,OAAA;IDT8C;;;;AAiBlD;ICFI,GAAA;EAAA;EDE6B;;;;;;;AAwBjC;;;;;;ECXE,MAAA,GAAS,oBAAA;;AAjDX;;;;;;EA0DE,SAAA;IAvCE;;;;;;;;IAgDA,eAAA;IAUa;AAIjB;;;;;;;IAJI,aAAA;EAAA;AAAA;AAAA,KAIQ,YAAA;EACV,cAAA;IAAkB,IAAA;EAAA;EAClB,WAAA;AAAA;AAAA,KAGU,YAAA,WAAuB,eAAA,KAAoB,OAAA,EAAS,CAAA;;;AAQhE;;;;UAAiB,oBAAA;EAgCsD;;;;;;;;;;;EApBrE,UAAA,IAAc,OAAA;EAwD8B;;;;;;EAAA,IAhDxC,OAAA;EAkD2E;;;;;;;;;;EAtC/E,QAAA,WAAmB,eAAA,EAAiB,SAAA,UAAmB,YAAA,EAAc,CAAA,EAAG,OAAA,GAAU,qBAAA,GAAwB,CAAA;EAAxB;;;;;;;;;;;;EAclF,KAAA,WAAgB,eAAA,EACd,SAAA,UACA,YAAA,EAAc,CAAA,EACd,QAAA,EAAU,YAAA,CAAa,CAAA,GACvB,OAAA,GAAU,qBAAA;EAAV;;;;;;;;EAWF,OAAA,WAAkB,eAAA,EAAiB,SAAA,UAAmB,QAAA,GAAW,YAAA,CAAa,CAAA;EAO9E;;;EAFA,UAAA;EAEA,EAAA,iBAAmB,YAAA,EAAc,SAAA,EAAW,CAAA,EAAG,OAAA,GAAU,IAAA,EAAM,YAAA,CAAa,CAAA;EAE5E,GAAA,iBAAoB,YAAA,EAAc,SAAA,EAAW,CAAA,EAAG,OAAA,IAAW,IAAA,EAAM,YAAA,CAAa,CAAA;EAFF;;;;;;EAU5E,OAAA;AAAA;;;cClKW,oBAAA,YAAgC,oBAAA;EAAA,iBAGd,KAAA;EAAA,iBAAoD,SAAA;EAAA,iBAFhE,aAAA;cAEY,KAAA,EAAO,0BAAA,EAA6C,SAAA,EAAW,iCAAA;EAM5F,KAAA,CAAM,OAAA,aAAoB,IAAA;EAI1B,IAAA,CAAK,OAAA,aAAoB,IAAA;EAIzB,IAAA,CAAK,OAAA,aAAoB,IAAA;EAIzB,KAAA,CAAM,OAAA,aAAoB,IAAA;EAAA,QAIlB,GAAA;AAAA;;;cCnDG,YAAA,GACX,YAAA,UACA,aAAA,GAAgB,2BAAA,KACf,oBAAA;AAAA,cAQU,mBAAA,GACX,KAAA,EAAO,0BAAA,EACP,gBAAA,GAAmB,iCAAA,KAAiC,oBAAA"}
|
|
@@ -74,6 +74,8 @@ type ConfigDirectorClientOptions = {
|
|
|
74
74
|
* If streaming is enabled, the operation (initialization or context update) may still succeed
|
|
75
75
|
* after it times out if no unrecoverable errors are encountered (like an invalid SDK key).
|
|
76
76
|
* If streaming is disabled, if the operation times out, it will not be retried.
|
|
77
|
+
*
|
|
78
|
+
* Defaults to 3,000 milliseconds (3 seconds)
|
|
77
79
|
*/
|
|
78
80
|
timeout?: number;
|
|
79
81
|
/**
|
|
@@ -97,6 +99,33 @@ type ConfigDirectorClientOptions = {
|
|
|
97
99
|
* );
|
|
98
100
|
*/
|
|
99
101
|
logger?: ConfigDirectorLogger;
|
|
102
|
+
/**
|
|
103
|
+
* Telemetry tunning. It is unlikely these settings need to be adjusted. However, in cases where
|
|
104
|
+
* your application has a large number of evaluations per second, you can adjust these settings
|
|
105
|
+
* to tune the memory footprint and frequency of telemetry requests.
|
|
106
|
+
* Keep in mind that ConfigDirector relies on these telemetry events to provide insights and features
|
|
107
|
+
* related to the configs being used.
|
|
108
|
+
*/
|
|
109
|
+
telemetry?: {
|
|
110
|
+
/**
|
|
111
|
+
* The size limit of telemetry event queues. If the size limit is reached before the events are
|
|
112
|
+
* flushed to the network, events will be dropped. ConfigDirector keeps a count of dropped events
|
|
113
|
+
* if the number of dropped events is higher than 50% of the total events, ConfigDirector will
|
|
114
|
+
* issue a notification alert in the dashboard.
|
|
115
|
+
*
|
|
116
|
+
* A number between 100 and 100,000. Defaults to 5,000.
|
|
117
|
+
*/
|
|
118
|
+
eventQueueLimit?: number;
|
|
119
|
+
/**
|
|
120
|
+
* How often events are flushed and sent over the network in milliseconds.
|
|
121
|
+
*
|
|
122
|
+
* Decrease this number if your application consistently captures a large number of events in short
|
|
123
|
+
* periods of time in order to reduce memory footprint from a large event queue.
|
|
124
|
+
*
|
|
125
|
+
* Defaults to 30,000 milliseconds (30 seconds)
|
|
126
|
+
*/
|
|
127
|
+
flushInterval?: number;
|
|
128
|
+
};
|
|
100
129
|
};
|
|
101
130
|
type ClientEvents = {
|
|
102
131
|
configsUpdated: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configdirector-server.d.mts","names":[],"sources":["../../shared/src/types.ts","../src/types.ts","../../shared/src/logger.ts","../src/api.ts"],"mappings":";KAYY,kBAAA;EAAA,CAAwB,GAAA;AAAA;AAAA,KAExB,eAAA,wCAAuD,kBAAA;AAAA,KAcvD,0BAAA;AAAA,UAEK,oBAAA;EACf,KAAA,CAAM,OAAA,aAAoB,IAAA;EAE1B,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,KAAA,CAAM,OAAA,aAAoB,IAAA;AAAA;AAAA,UAGX,iCAAA;EACf,eAAA,CAAgB,OAAA;AAAA;;;;;KAgBN,qBAAA;EApBoB;;AAGhC;;;;;AAiBA;EASE,EAAA;;;;;EAMA,IAAA;EAMY;;;AAGd;EAHE,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA;AAAA,KAGF,yBAAA;EACV,UAAA;EACA,OAAA;AAAA;;;;;;KC9DU,2BAAA;EDPe;;;ECWzB,QAAA,GAAW,yBAAA;EDGD;;;ECCV,UAAA;IDDoC;AAEtC;;;;;;;;;ICUI,SAAA;IDLF
|
|
1
|
+
{"version":3,"file":"configdirector-server.d.mts","names":[],"sources":["../../shared/src/types.ts","../src/types.ts","../../shared/src/logger.ts","../src/api.ts"],"mappings":";KAYY,kBAAA;EAAA,CAAwB,GAAA;AAAA;AAAA,KAExB,eAAA,wCAAuD,kBAAA;AAAA,KAcvD,0BAAA;AAAA,UAEK,oBAAA;EACf,KAAA,CAAM,OAAA,aAAoB,IAAA;EAE1B,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,IAAA,CAAK,OAAA,aAAoB,IAAA;EAEzB,KAAA,CAAM,OAAA,aAAoB,IAAA;AAAA;AAAA,UAGX,iCAAA;EACf,eAAA,CAAgB,OAAA;AAAA;;;;;KAgBN,qBAAA;EApBoB;;AAGhC;;;;;AAiBA;EASE,EAAA;;;;;EAMA,IAAA;EAMY;;;AAGd;EAHE,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA;AAAA,KAGF,yBAAA;EACV,UAAA;EACA,OAAA;AAAA;;;;;;KC9DU,2BAAA;EDPe;;;ECWzB,QAAA,GAAW,yBAAA;EDGD;;;ECCV,UAAA;IDDoC;AAEtC;;;;;;;;;ICUI,SAAA;IDLF;;;;;;;;ICcE,OAAA;IDT8C;;;;AAiBlD;ICFI,GAAA;EAAA;EDE6B;;;;;;;AAwBjC;;;;;;ECXE,MAAA,GAAS,oBAAA;;AAjDX;;;;;;EA0DE,SAAA;IAvCE;;;;;;;;IAgDA,eAAA;IAUa;AAIjB;;;;;;;IAJI,aAAA;EAAA;AAAA;AAAA,KAIQ,YAAA;EACV,cAAA;IAAkB,IAAA;EAAA;EAClB,WAAA;AAAA;AAAA,KAGU,YAAA,WAAuB,eAAA,KAAoB,OAAA,EAAS,CAAA;;;AAQhE;;;;UAAiB,oBAAA;EAgCsD;;;;;;;;;;;EApBrE,UAAA,IAAc,OAAA;EAwD8B;;;;;;EAAA,IAhDxC,OAAA;EAkD2E;;;;;;;;;;EAtC/E,QAAA,WAAmB,eAAA,EAAiB,SAAA,UAAmB,YAAA,EAAc,CAAA,EAAG,OAAA,GAAU,qBAAA,GAAwB,CAAA;EAAxB;;;;;;;;;;;;EAclF,KAAA,WAAgB,eAAA,EACd,SAAA,UACA,YAAA,EAAc,CAAA,EACd,QAAA,EAAU,YAAA,CAAa,CAAA,GACvB,OAAA,GAAU,qBAAA;EAAV;;;;;;;;EAWF,OAAA,WAAkB,eAAA,EAAiB,SAAA,UAAmB,QAAA,GAAW,YAAA,CAAa,CAAA;EAO9E;;;EAFA,UAAA;EAEA,EAAA,iBAAmB,YAAA,EAAc,SAAA,EAAW,CAAA,EAAG,OAAA,GAAU,IAAA,EAAM,YAAA,CAAa,CAAA;EAE5E,GAAA,iBAAoB,YAAA,EAAc,SAAA,EAAW,CAAA,EAAG,OAAA,IAAW,IAAA,EAAM,YAAA,CAAa,CAAA;EAFF;;;;;;EAU5E,OAAA;AAAA;;;cClKW,oBAAA,YAAgC,oBAAA;EAAA,iBAGd,KAAA;EAAA,iBAAoD,SAAA;EAAA,iBAFhE,aAAA;cAEY,KAAA,EAAO,0BAAA,EAA6C,SAAA,EAAW,iCAAA;EAM5F,KAAA,CAAM,OAAA,aAAoB,IAAA;EAI1B,IAAA,CAAK,OAAA,aAAoB,IAAA;EAIzB,IAAA,CAAK,OAAA,aAAoB,IAAA;EAIzB,KAAA,CAAM,OAAA,aAAoB,IAAA;EAAA,QAIlB,GAAA;AAAA;;;cCnDG,YAAA,GACX,YAAA,UACA,aAAA,GAAgB,2BAAA,KACf,oBAAA;AAAA,cAQU,mBAAA,GACX,KAAA,EAAO,0BAAA,EACP,gBAAA,GAAmB,iCAAA,KAAiC,oBAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createEventSource as e}from"eventsource-client";import t,{EventEmitter as n}from"node:events";import{findByPointer as r}from"@jsonjoy.com/json-pointer";import*as i from"semver";import{rapidhash as a}from"rapidhash-js";var o=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}},c=class{constructor(e){this.options=e,this.eventEmitter=new n,this.options=e,this.logger=e.logger,this.url=new URL(`server/sse/v1`,e.baseUrl)}async connect(t){this.eventSource&&this.close();let n,r,i=async(e,t)=>{let i=await fetch(e,t);return n=i.status,i.ok||(r=await i.text()),i},a=new Promise((t,a)=>{this.eventSource=e({url:this.url,fetch:i,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({metaContext:this.options.metaContext,serverSdkKey:this.options.serverSdkKey}),onMessage:({data:e})=>{this.dispatchMessage(e)},onConnect:()=>{n&&this.isStatusFatal(n)?(this.close(),a(this.prepareFatalError(n,r))):(this.logger.debug(`[EventSourceTransport] Connected, status: %s`,n),t(this))},onScheduleReconnect:e=>{n&&this.isStatusFatal(n)?(this.close(),a(this.prepareFatalError(n,r))):this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${e.delay}. Response status: ${n}`)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}})});return Promise.race([a,new Promise(e=>{setTimeout(()=>e(this),t)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configBundleReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,t){let n=e??0;return new o(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}isStatusFatal(e){return!!e&&e>=400&&e<500}get isConnected(){return this.eventSource?.readyState==`open`}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}clear(){this.eventEmitter.removeAllListeners()}close(){this.eventSource?.close()}dispose(){this.close(),this.clear()}};const l=(e,t,n)=>{switch(t.toLowerCase()){case`=`:case`equals`:return e==n[0];case`!=`:case`does not equal`:return e!=n[0];case`is one of`:return n.includes(e);case`is not one of`:return!n.includes(e);case`starts with any of`:return n.findIndex(t=>e?.startsWith(t))>=0;case`does not start with any of`:return n.findIndex(t=>e?.startsWith(t))<0;case`ends with any of`:return n.findIndex(t=>e?.endsWith(t))>=0;case`does not end with any of`:return n.findIndex(t=>e?.endsWith(t))<0;case`matches regex`:return u(n[0],e);case`does not match regex`:return!u(n[0],e);default:return!1}},u=(e,t)=>{try{return new RegExp(e).test(t)}catch{return!1}},d=(e,t,n)=>{if(typeof e==`boolean`)return!1;let r=t.toLowerCase();if(e==null)return r==`!=`||r==`does not equal`;let{value:i,target:a}=f(e,n[0]);switch(r){case`=`:case`equals`:return i==a;case`!=`:case`does not equal`:return i!=a;case`<`:return i<a;case`<=`:return i<=a;case`>`:return i>a;case`>=`:return i>=a;default:return!1}},f=(e,t)=>typeof e==`number`?{value:e,target:p(t)}:typeof e==`bigint`?{value:e,target:BigInt(t)}:{value:p(e),target:p(t)},p=e=>{let t=Number.parseFloat(e.toString());if(isNaN(t)||!isFinite(t))throw TypeError(`Invalid numeric value: ${e}`);return t},m=(e,t,n)=>{if(e==null)return!1;try{let r=new Date(e),i=new Date(n[0]);switch(t){case`is after`:return r>i;case`is before`:return r<i;default:return!1}}catch{return!1}},h=(e,t,n)=>{let r=t.toLowerCase();if(e==null||e.trim().length==0)return r==`is not one of`;let a=i.coerce(e)??e,o=n.map(e=>i.coerce(e)??e);try{switch(r){case`=`:return g(a,o[0]);case`<`:return i.lt(a,o[0]);case`<=`:return i.lte(a,o[0]);case`>`:return i.gt(a,o[0]);case`>=`:return i.gte(a,o[0]);case`is one of`:return o.findIndex(e=>g(a,e))>=0;case`is not one of`:return o.findIndex(e=>g(a,e))<0;default:return!1}}catch{return!1}},g=(e,t)=>{try{return i.eq(e,t)}catch{return!1}},_=(e,t,n)=>{let r=t.toLowerCase();if(!Array.isArray(e))return r==`does not contain any of`;switch(r){case`contains any of`:return e.some(e=>n.includes(e));case`does not contain any of`:return!e.some(e=>n.includes(e));default:return!1}};var v=class{evaluate(e,t){if(!this.isConditionValidForComparison(e))return!1;switch(e.attribute){case`identifier`:return this.compare(t?.context?.id,e);case`name`:return this.compare(t?.context?.name,e);case`appName`:return this.compare(t?.metadata?.appName,e);case`appVersion`:return this.compare(t?.metadata?.appVersion,e);case`traits`:if(e.trait){let n=this.findTraitValue(t?.context?.traits,e.trait);return this.compare(n,e)}}return!1}compare(e,t){switch(t.targetType){case`text`:return l(e?.toString(),t.operator,t.targetValues);case`number`:return d(e,t.operator,t.targetValues);case`datetime`:return m(e?.toString(),t.operator,t.targetValues);case`semver`:return h(e?.toString(),t.operator,t.targetValues);case`array`:return _(e,t.operator,t.targetValues)}return!1}findTraitValue(e,t){try{return r(t,e).val}catch{return}}isConditionValidForComparison(e){return Array.isArray(e.targetValues)&&e.targetValues.length>0}};const y=e=>{let t=`${e.contextIdentifier}-${e.configId}`;return Number(a(t,{seed:15426857351n})%1000n)/10};var b=class{constructor(e){if(this.logger=e,this.conditionEvaluator=new v,!e||!e.warn||!e.error||!e.info)throw TypeError(`The provided logger is not a valid 'ConfigDirectorLogger'`);this.logger=e}evaluate(e,t){return{id:e.id,key:e.key,type:e.type,value:this.getConfigValue(e,t)}}getConfigValue(e,t){let n=[...e.target?.rules??[]].sort((e,t)=>(e.order??2**53-1)-(t.order??2**53-1));for(let r of n){let n=this.evaluateRule(r,e,t);if(n.success)return n.value}return e.target?.defaultValue}evaluateRule(e,t,n){try{if(e.type==`percentage`)return this.evaluatePercentageRule(e,t,n);if(e.type==`conditional`)return this.evaluateConditionalRule(e,t,n)}catch(n){this.logger.warn(`There was an error while evaluating a targeting rule '${e?.id}' for '${t?.key}'. The rule will be disregarded.`,{error:n,configKey:t?.key,ruleId:e?.id})}return{success:!1}}evaluatePercentageRule(e,t,n){return this.evaluatePercentage(e.percentages??[],t,n)}evaluatePercentage(e,t,n){let r=y({configId:t.id,contextIdentifier:n?.context?.id??crypto.randomUUID()}),i=0,a=0,o;for(;!o&&i<e.length;)r<=e[i].percentage+a&&(o=e[i]),a+=e[i].percentage,i+=1;return o?.value==null?{success:!1}:{success:!0,value:o.value.toString()}}evaluateConditionalRule(e,t,n){let r=e.conditions??[],i=0,a;for(;!a&&i<r.length;)this.conditionEvaluator.evaluate(r[i],n)&&(a=r[i]),i+=1;return a&&e.target==`value`&&e.value!=null?{success:!0,value:e.value.toString()}:a&&e.target==`percentage`?this.evaluatePercentage(e.percentages??[],t,n):{success:!1}}};const x=e=>{let t=typeof e;if(t===`object`)try{return e.constructor?.name??t}catch{return t}else if(t===`function`)try{let n=e.name;return n?`function: ${n}`:t}catch{return t}return t},S=e=>e===`number`||e===`bigint`,C=(e,t)=>{let n=e.value,r=x(t);if(!n)return{parsedValue:t,requestedType:r,usedDefault:!0,reason:`value-missing`};if(typeof t==`string`)return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`};if(typeof t==`boolean`&&e.type===`boolean`){let e=w(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(S(typeof t)&&e.type===`integer`){let e=T(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(S(typeof t)&&(e.type===`float`||e.type===`enum`)){let e=E(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`}},w=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},T=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},E=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},D={off:-1,error:0,warn:1,info:2,debug:3},O=()=>{let e={year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,timeZoneName:`short`};try{return new Intl.DateTimeFormat(void 0,{...e,fractionalSecondDigits:3})}catch{return new Intl.DateTimeFormat(void 0,e)}};var k=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=O()}debug(e,...t){this.log(console.debug,`debug`,e,...t)}info(e,...t){this.log(console.info,`info`,e,...t)}warn(e,...t){this.log(console.warn,`warn`,e,...t)}error(e,...t){this.log(console.error,`error`,e,...t)}log(e,t,n,...r){D[this.level]>=D[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},A=class{decorateMessage(e){return`[ConfigDirector:js-server-sdk] ${e}`}};const j=(e,t)=>new k(e??`warn`,t??new A),M=e=>N(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),N=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},P=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},F=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},I=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(t,{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var L=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`server/telemetry/v1`,e.baseUrl)}async report({discreteEvents:e,aggregatedEvents:t,droppedEvents:n}){if(!this.executeRequests)return{success:!1,fatalError:!0};let r={serverSdkKey:this.sdkKey,discreteEvents:e,aggregatedEvents:t,droppedEvents:n};if(this.isReportEmpty(r))return{success:!0,fatalError:!1};let i=await this.sendReport(r);return i.fatalError&&(this.executeRequests=!1),i}isReportEmpty(e){return P(e.discreteEvents)&&P(e.aggregatedEvents)&&F(e.droppedEvents)}async sendReport(e){try{let t=await I(5e3,this.url,{method:`POST`,body:JSON.stringify(e)},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:!1}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},R=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},z=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||(this.startTime=new Date);let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},B=class{constructor(e){this.evaluationEventQueue=new z,this.aggregator=new R,this.collectEvents=!0,this.logger=e.logger,this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(e,t){if(t===`json`)try{return M(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot();return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},V=class extends B{constructor(e){super(e),this.reporter=new L(e)}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(e))}sanitizeEvaluatedConfigEvent(e){return{key:e.key,type:e.type,defaultValue:this.sanitizeValue(e.defaultValue,e.type),requestedType:e.requestedType,evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type),usedDefault:e.usedDefault,evaluationReason:e.evaluationReason}}};const H=new URL(`https://server-sdk-api.configdirector.com`);var U=class{constructor(e,n,r){this.handlersMap=new Map,this.eventEmitter=new t,this.ready=!1,this.logger=r?.logger??j(),this.timeout=r?.connection?.timeout??3e3,this.metaContext=r?.metadata??{},this.configEvaluator=new b(this.logger);let i=this.parseUrl(r?.connection?.url)??H;this.streaming=r?.connection?.streaming!==!1;let a=c;this.usageEventCollector=new V({sdkKey:e,logger:this.logger,baseUrl:i}),this.transport=new a({serverSdkKey:e,baseUrl:i,metaContext:{...r?.metadata,sdkName:n.sdkName,sdkVersion:n.sdkVersion},logger:this.logger}),this.transport.on(`configBundleReceived`,e=>{this.readyResolve?.();let t=Object.keys(e.configs);!this.configSet||e.kind==`full`?(this.configSet=e,this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)):(this.configSet.configs={...this.configSet.configs,...e.configs},this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)),this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server:`,{keys:t})})}async initialize(){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.eventEmitter.emit(`clientReady`),this.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let e=new Date().getTime();await this.transport.connect(this.timeout);let t=new Date().getTime()-e,n=this.timeout-t;if(n>0&&await Promise.race([this.readyPromise,new Promise(e=>{setTimeout(()=>e(),n)})]),!this.ready){let e=this.streaming?`The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`:`Since the client was configured without streaming, configs may not update and always return the default value.`;this.logger.warn(`[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${e}`)}}catch(e){this.logger.error(`[ConfigDirectorClient] An error occurred during initialization: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){this.handlersMap.get(e.key)?.forEach(t=>{let n=this.getValueFromConfigDefinition(e.key,e,t.defaultValue,t.context);t.handler(n)})}watch(e,t,n,r){this.validateDefaultValue(t);let i=this.handlersMap.get(e),a={handler:n,defaultValue:t,requestedType:typeof t,context:r};return i?i.push(a):this.handlersMap.set(e,[a]),()=>this.unwatch(e,n)}unwatch(e,t){let n=this.handlersMap.get(e);if(n)if(t){let e=n.findIndex(e=>e.handler==t);e>=0&&n?.splice(e,1)}else n.splice(0)}getValue(e,t,n){this.validateDefaultValue(t);let r=this.configSet?.configs[e];return this.getValueFromConfigDefinition(e,r,t,n)}getValueFromConfigDefinition(e,t,n,r){if(t){let i=C(this.configEvaluator.evaluate(t,{context:r,metadata:this.metaContext}),n);return this.usageEventCollector.evaluatedConfig({contextId:r?.id,key:e,defaultValue:n,requestedType:i.requestedType,evaluatedValue:i.parsedValue,usedDefault:i.usedDefault,evaluationReason:i.reason}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${i.parsedValue}'`),i.parsedValue}else return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({contextId:r?.id,key:e,defaultValue:n,requestedType:x(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new s(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new s(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new s(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}removeAllObservers(){this.logger.debug(`[ConfigDirectorClient] removeAllObservers() has been called, removing all observers`),this.eventEmitter.removeAllListeners(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}closeConnection(){this.logger.debug(`[ConfigDirectorClient] closeConnection() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.removeAllObservers(),this.closeConnection()}};const W=(e,t)=>new U(e,{sdkName:`js-server-sdk`,sdkVersion:`0.1.0`},t),G=(e,t)=>j(e,t);export{W as createClient,G as createConsoleLogger};
|
|
1
|
+
import{createEventSource as e}from"eventsource-client";import t,{EventEmitter as n}from"node:events";import{findByPointer as r}from"@jsonjoy.com/json-pointer";import*as i from"semver";import{rapidhash as a}from"rapidhash-js";var o=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}},c=class{constructor(e){this.options=e,this.eventEmitter=new n,this.options=e,this.logger=e.logger,this.url=new URL(`server/sse/v1`,e.baseUrl)}async connect(t){this.eventSource&&this.close();let n,r,i=async(e,t)=>{let i=await fetch(e,t);return n=i.status,i.ok||(r=await i.text()),i},a=new Promise((t,a)=>{this.eventSource=e({url:this.url,fetch:i,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({metaContext:this.options.metaContext,serverSdkKey:this.options.serverSdkKey}),onMessage:({data:e})=>{this.dispatchMessage(e)},onConnect:()=>{n&&this.isStatusFatal(n)?(this.close(),a(this.prepareFatalError(n,r))):(this.logger.debug(`[EventSourceTransport] Connected, status: ${n}`),t(this))},onScheduleReconnect:e=>{n&&this.isStatusFatal(n)?(this.close(),a(this.prepareFatalError(n,r))):this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${e.delay}. Response status: ${n}`)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}})});return Promise.race([a,new Promise(e=>{setTimeout(()=>e(this),t)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configBundleReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,t){let n=e??0;return new o(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}isStatusFatal(e){return!!e&&e>=400&&e<500}get isConnected(){return this.eventSource?.readyState==`open`}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}clear(){this.eventEmitter.removeAllListeners()}close(){this.eventSource?.close()}dispose(){this.close(),this.clear()}};const l=(e,t,n)=>{switch(t.toLowerCase()){case`=`:case`equals`:return e==n[0];case`!=`:case`does not equal`:return e!=n[0];case`is one of`:return n.includes(e);case`is not one of`:return!n.includes(e);case`starts with any of`:return n.findIndex(t=>e?.startsWith(t))>=0;case`does not start with any of`:return n.findIndex(t=>e?.startsWith(t))<0;case`ends with any of`:return n.findIndex(t=>e?.endsWith(t))>=0;case`does not end with any of`:return n.findIndex(t=>e?.endsWith(t))<0;case`matches regex`:return u(n[0],e);case`does not match regex`:return!u(n[0],e);default:return!1}},u=(e,t)=>{try{return new RegExp(e).test(t)}catch{return!1}},d=(e,t,n)=>{if(typeof e==`boolean`)return!1;let r=t.toLowerCase();if(e==null)return r==`!=`||r==`does not equal`;let{value:i,target:a}=f(e,n[0]);switch(r){case`=`:case`equals`:return i==a;case`!=`:case`does not equal`:return i!=a;case`<`:return i<a;case`<=`:return i<=a;case`>`:return i>a;case`>=`:return i>=a;default:return!1}},f=(e,t)=>typeof e==`number`?{value:e,target:p(t)}:typeof e==`bigint`?{value:e,target:BigInt(t)}:{value:p(e),target:p(t)},p=e=>{let t=Number.parseFloat(e.toString());if(isNaN(t)||!isFinite(t))throw TypeError(`Invalid numeric value: ${e}`);return t},m=(e,t,n)=>{if(e==null)return!1;try{let r=new Date(e),i=new Date(n[0]);switch(t){case`is after`:return r>i;case`is before`:return r<i;default:return!1}}catch{return!1}},h=(e,t,n)=>{let r=t.toLowerCase();if(e==null||e.trim().length==0)return r==`is not one of`;let a=i.coerce(e)??e,o=n.map(e=>i.coerce(e)??e);try{switch(r){case`=`:return g(a,o[0]);case`<`:return i.lt(a,o[0]);case`<=`:return i.lte(a,o[0]);case`>`:return i.gt(a,o[0]);case`>=`:return i.gte(a,o[0]);case`is one of`:return o.findIndex(e=>g(a,e))>=0;case`is not one of`:return o.findIndex(e=>g(a,e))<0;default:return!1}}catch{return!1}},g=(e,t)=>{try{return i.eq(e,t)}catch{return!1}},_=(e,t,n)=>{let r=t.toLowerCase();if(!Array.isArray(e))return r==`does not contain any of`;switch(r){case`contains any of`:return e.some(e=>n.includes(e));case`does not contain any of`:return!e.some(e=>n.includes(e));default:return!1}};var v=class{evaluate(e,t){if(!this.isConditionValidForComparison(e))return!1;switch(e.attribute){case`identifier`:return this.compare(t?.context?.id,e);case`name`:return this.compare(t?.context?.name,e);case`appName`:return this.compare(t?.metadata?.appName,e);case`appVersion`:return this.compare(t?.metadata?.appVersion,e);case`traits`:if(e.trait){let n=this.findTraitValue(t?.context?.traits,e.trait);return this.compare(n,e)}}return!1}compare(e,t){switch(t.targetType){case`text`:return l(e?.toString(),t.operator,t.targetValues);case`number`:return d(e,t.operator,t.targetValues);case`datetime`:return m(e?.toString(),t.operator,t.targetValues);case`semver`:return h(e?.toString(),t.operator,t.targetValues);case`array`:return _(e,t.operator,t.targetValues)}return!1}findTraitValue(e,t){try{return r(t,e).val}catch{return}}isConditionValidForComparison(e){return Array.isArray(e.targetValues)&&e.targetValues.length>0}};const y=e=>{let t=`${e.contextIdentifier}-${e.configId}`;return Number(a(t,{seed:15426857351n})%1000n)/10};var b=class{constructor(e){if(this.logger=e,this.conditionEvaluator=new v,!e||!e.warn||!e.error||!e.info)throw TypeError(`The provided logger is not a valid 'ConfigDirectorLogger'`);this.logger=e}evaluate(e,t){return{id:e.id,key:e.key,type:e.type,value:this.getConfigValue(e,t)}}getConfigValue(e,t){let n=[...e.target?.rules??[]].sort((e,t)=>(e.order??2**53-1)-(t.order??2**53-1));for(let r of n){let n=this.evaluateRule(r,e,t);if(n.success)return n.value}return e.target?.defaultValue}evaluateRule(e,t,n){try{if(e.type==`percentage`)return this.evaluatePercentageRule(e,t,n);if(e.type==`conditional`)return this.evaluateConditionalRule(e,t,n)}catch(n){this.logger.warn(`There was an error while evaluating a targeting rule '${e?.id}' for '${t?.key}'. The rule will be disregarded.`,{error:n,configKey:t?.key,ruleId:e?.id})}return{success:!1}}evaluatePercentageRule(e,t,n){return this.evaluatePercentage(e.percentages??[],t,n)}evaluatePercentage(e,t,n){let r=y({configId:t.id,contextIdentifier:n?.context?.id??crypto.randomUUID()}),i=0,a=0,o;for(;!o&&i<e.length;)r<=e[i].percentage+a&&(o=e[i]),a+=e[i].percentage,i+=1;return o?.value==null?{success:!1}:{success:!0,value:o.value.toString()}}evaluateConditionalRule(e,t,n){let r=e.conditions??[],i=0,a;for(;!a&&i<r.length;)this.conditionEvaluator.evaluate(r[i],n)&&(a=r[i]),i+=1;return a&&e.target==`value`&&e.value!=null?{success:!0,value:e.value.toString()}:a&&e.target==`percentage`?this.evaluatePercentage(e.percentages??[],t,n):{success:!1}}};const x=e=>{let t=typeof e;if(t===`object`)try{return e.constructor?.name??t}catch{return t}else if(t===`function`)try{let n=e.name;return n?`function: ${n}`:t}catch{return t}return t},S=e=>e===`number`||e===`bigint`,C=(e,t)=>{let n=e.value,r=x(t);if(!n)return{parsedValue:t,requestedType:r,usedDefault:!0,reason:`value-missing`};if(typeof t==`string`)return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`};if(typeof t==`boolean`&&e.type===`boolean`){let e=w(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(S(typeof t)&&e.type===`integer`){let e=T(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(S(typeof t)&&(e.type===`float`||e.type===`enum`)){let e=E(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}return{parsedValue:n,requestedType:r,usedDefault:!1,reason:`found-match`}},w=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},T=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},E=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},D={off:-1,error:0,warn:1,info:2,debug:3},O=()=>{let e={year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,timeZoneName:`short`};try{return new Intl.DateTimeFormat(void 0,{...e,fractionalSecondDigits:3})}catch{return new Intl.DateTimeFormat(void 0,e)}};var k=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=O()}debug(e,...t){this.log(console.debug,`debug`,e,...t)}info(e,...t){this.log(console.info,`info`,e,...t)}warn(e,...t){this.log(console.warn,`warn`,e,...t)}error(e,...t){this.log(console.error,`error`,e,...t)}log(e,t,n,...r){D[this.level]>=D[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},A=class{decorateMessage(e){return`[ConfigDirector:js-server-sdk] ${e}`}};const j=(e,t)=>new k(e??`warn`,t??new A),M=e=>N(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),N=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},P=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]?.length??0)>0)return!1;return!0},F=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let n of t)if((e[n]??0)>0)return!1;return!0},I=async(e,t,n,r)=>{let i=new AbortController,a=setTimeout(()=>{r.debug(`[fetchWithTimeout] Reached timeout, aborting request`),i.abort()},e);try{let e=await fetch(t,{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var L=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`server/telemetry/v1`,e.baseUrl)}async report({discreteEvents:e,aggregatedEvents:t,droppedEvents:n}){if(!this.executeRequests)return{success:!1,fatalError:!0};let r={serverSdkKey:this.sdkKey,discreteEvents:e,aggregatedEvents:t,droppedEvents:n};if(this.isReportEmpty(r))return{success:!0,fatalError:!1};let i=await this.sendReport(r);return i.fatalError&&(this.executeRequests=!1),i}isReportEmpty(e){return P(e.discreteEvents)&&P(e.aggregatedEvents)&&F(e.droppedEvents)}async sendReport(e){try{let t=await I(5e3,this.url,{method:`POST`,body:JSON.stringify(e)},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:!1}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},R=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let n of e.events){let e=JSON.stringify(n),r=t.get(e)?.count||0;t.set(e,{event:n,count:r+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},z=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e??1e3}get events(){return this._events}get reachedLimit(){return this._events.length>=this.limit}get droppedEventCount(){return this._droppedEventCount}push(...e){this.startTime||(this.startTime=new Date);let t=Math.max(0,e.length-this.limit),n=e.slice(t,e.length);if(this._droppedEventCount+=t,this._events.length+n.length>this.limit){let e=this._events.length+n.length-this.limit;this._events.splice(0,e),this._droppedEventCount+=e}return this._events.push(...n)}takeSnapshot(){let e=new Date,t=this.startTime??e,n=this._events.splice(0),r=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:t,endTime:e,events:n,droppedCount:r}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},B=class{constructor(e){this.aggregator=new R,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new z(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(e,t){if(t===`json`)try{return M(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)}async flushAndScheduleNext(){(await this.flush()).fatalError?(this.collectEvents=!1,this.close(),this.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),this.flushIntervalDelay)}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot();return await this.reporter?.report({context:this._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount}})}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},V=class extends Map{constructor(e,t){super(t),this._droppedCount=0,this.limit=e,this.enforceLimit()}set(e,t){return super.set(e,t),this.enforceLimit(),this}get droppedCount(){return this._droppedCount}clearAndReset(){this.clear(),this._droppedCount=0}enforceLimit(){for(;this.size>this.limit;){let e=this.keys().next().value;e!==void 0&&(this.delete(e),this._droppedCount+=1)}}},H=class extends B{constructor(e){super(e),this.reporter=new L(e),this.contexts=new V(e.contextLimit??1e3)}evaluatedConfig(e){if(!this.collectEvents)return;e.context?.id&&this.contexts.set(e.context.id,e.context);let t={...e.evaluation,contextId:e.context?.id};this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(t))}sanitizeEvaluatedConfigEvent(e){return{...e,defaultValue:this.sanitizeValue(e.defaultValue,e.type),evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type)}}snapshotContexts(){let e=Array.from(this.contexts.values()),t=this.contexts.droppedCount;return this.contexts.clearAndReset(),{contexts:e,droppedCount:t}}async flush(){if(!this.reporter)return{success:!1,fatalError:!1};let e=this.evaluationEventQueue.takeSnapshot(),t=this.snapshotContexts();return await this.reporter?.report({context:this._context,discreteEvents:{capturedContexts:t.contexts},aggregatedEvents:{evaluatedConfig:this.aggregator.aggregate(e)},droppedEvents:{evaluatedConfig:e.droppedCount,capturedContexts:t.droppedCount}})}};const U=new URL(`https://server-sdk-api.configdirector.com`);var W=class{constructor(e,n,r){this.handlersMap=new Map,this.eventEmitter=new t,this.ready=!1,this.logger=r?.logger??j(),this.timeout=r?.connection?.timeout??3e3,this.metaContext=r?.metadata??{},this.configEvaluator=new b(this.logger);let i=this.parseUrl(r?.connection?.url)??U;this.streaming=r?.connection?.streaming!==!1;let a=c,o=r?.telemetry?.eventQueueLimit??5e3;this.usageEventCollector=new H({sdkKey:e,logger:this.logger,baseUrl:i,flushIntervalDelay:r?.telemetry?.flushInterval??3e4,evaluationQueueLimit:Math.ceil(o*.7),contextLimit:Math.floor(o*.3)}),this.transport=new a({serverSdkKey:e,baseUrl:i,metaContext:{...r?.metadata,sdkName:n.sdkName,sdkVersion:n.sdkVersion},logger:this.logger}),this.transport.on(`configBundleReceived`,e=>{this.readyResolve?.();let t=Object.keys(e.configs);!this.configSet||e.kind==`full`?(this.configSet=e,this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)):(this.configSet.configs={...this.configSet.configs,...e.configs},this.eventEmitter.emit(`configsUpdated`,{keys:t}),this.updateWatchers(e.configs)),this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server with ${t.length} key(s)`,{keys:t})})}async initialize(){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.eventEmitter.emit(`clientReady`),this.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let e=new Date().getTime();await this.transport.connect(this.timeout);let t=new Date().getTime()-e,n=this.timeout-t;if(n>0&&await Promise.race([this.readyPromise,new Promise(e=>{setTimeout(()=>e(),n)})]),!this.ready){let e=this.streaming?`The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`:`Since the client was configured without streaming, configs may not update and always return the default value.`;this.logger.warn(`[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${e}`)}}catch(e){this.logger.error(`[ConfigDirectorClient] An error occurred during initialization: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){this.handlersMap.get(e.key)?.forEach(t=>{let n=this.getValueFromConfigDefinition(e.key,e,t.defaultValue,t.context);t.handler(n)})}watch(e,t,n,r){this.validateDefaultValue(t);let i=this.handlersMap.get(e),a={handler:n,defaultValue:t,requestedType:typeof t,context:r};return i?i.push(a):this.handlersMap.set(e,[a]),()=>this.unwatch(e,n)}unwatch(e,t){let n=this.handlersMap.get(e);if(n)if(t){let e=n.findIndex(e=>e.handler==t);e>=0&&n?.splice(e,1)}else n.splice(0)}getValue(e,t,n){this.validateDefaultValue(t);let r=this.configSet?.configs[e];return this.getValueFromConfigDefinition(e,r,t,n)}getValueFromConfigDefinition(e,t,n,r){if(t){let i=C(this.configEvaluator.evaluate(t,{context:r,metadata:this.metaContext}),n);return this.usageEventCollector.evaluatedConfig({context:r,evaluation:{key:e,defaultValue:n,requestedType:i.requestedType,evaluatedValue:i.parsedValue,usedDefault:i.usedDefault,evaluationReason:i.reason}}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${i.parsedValue}'`),i.parsedValue}else return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({context:r,evaluation:{key:e,defaultValue:n,requestedType:x(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}}),n}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new s(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new s(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new s(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}on(e,t){this.eventEmitter.on(e,t)}off(e,t){t?this.eventEmitter.off(e,t):this.eventEmitter.removeAllListeners(e)}removeAllObservers(){this.logger.debug(`[ConfigDirectorClient] removeAllObservers() has been called, removing all observers`),this.eventEmitter.removeAllListeners(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}closeConnection(){this.logger.debug(`[ConfigDirectorClient] closeConnection() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.removeAllObservers(),this.closeConnection()}};const G=(e,t)=>new W(e,{sdkName:`js-server-sdk`,sdkVersion:`0.2.0`},t),K=(e,t)=>j(e,t);export{G as createClient,K as createConsoleLogger};
|
|
2
2
|
//# sourceMappingURL=configdirector-server.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configdirector-server.mjs","names":["EventEmitter"],"sources":["../../shared/src/errors.ts","../src/StreamingTransport.ts","../../config-evaluator/src/text-comparison.ts","../../config-evaluator/src/numeric-comparison.ts","../../config-evaluator/src/date-comparison.ts","../../config-evaluator/src/semver-comparison.ts","../../config-evaluator/src/array-comparison.ts","../../config-evaluator/src/ConditionEvaluator.ts","../../config-evaluator/src/percent-hashing.ts","../../config-evaluator/src/ConfigEvaluator.ts","../../shared/src/value-parser.ts","../../shared/src/logger.ts","../src/logger.ts","../../shared/src/telemetry/utils.ts","../../shared/src/fetchWithTimeout.ts","../src/telemetry/ServerEventReporter.ts","../../shared/src/telemetry/EventAggregator.ts","../../shared/src/telemetry/EventQueue.ts","../../shared/src/telemetry/TelemetryEventCollector.ts","../src/telemetry/ServerTelemetryEventCollector.ts","../src/DefaultConfigDirectorClient.ts","../src/api.ts"],"sourcesContent":["export class ConfigDirectorConnectionError extends Error {\n public override readonly name: string = \"ConfigDirectorConnectionError\";\n public readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.status = status;\n\n Object.setPrototypeOf(this, ConfigDirectorConnectionError.prototype);\n }\n}\n\nexport class ConfigDirectorValidationError extends Error {\n public override readonly name: string = \"ConfigDirectorValidationError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ConfigDirectorValidationError.prototype);\n }\n}\n","import { createEventSource, type EventSourceClient } from \"eventsource-client\";\nimport type {\n ConfigBundle,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { ConfigDirectorConnectionError } from \"@shared/errors\";\nimport { EventEmitter } from \"node:events\";\n\nexport class StreamingTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventSource: EventSourceClient | undefined;\n private eventEmitter = new EventEmitter();\n private url: URL;\n\n constructor(private readonly options: TransportOptions) {\n this.options = options;\n this.logger = options.logger;\n this.url = new URL(\"server/sse/v1\", options.baseUrl);\n }\n\n public async connect(timeout: number): Promise<this> {\n if (this.eventSource) {\n this.close();\n }\n\n let responseStatus: number | undefined = undefined;\n let errorBody: string | undefined = undefined;\n\n const customFetch = async (url: string | URL | Request, init?: RequestInit | undefined) => {\n const response = await fetch(url, init);\n responseStatus = response.status;\n if (!response.ok) {\n errorBody = await response.text();\n }\n return response;\n };\n\n const eventSourcePromise = new Promise<this>((resolve, reject) => {\n this.eventSource = createEventSource({\n url: this.url,\n fetch: customFetch,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n metaContext: this.options.metaContext,\n serverSdkKey: this.options.serverSdkKey,\n }),\n\n onMessage: ({ data }) => {\n this.dispatchMessage(data);\n },\n onConnect: () => {\n if (responseStatus && this.isStatusFatal(responseStatus)) {\n this.close();\n reject(this.prepareFatalError(responseStatus, errorBody));\n } else {\n this.logger.debug(\"[EventSourceTransport] Connected, status: %s\", responseStatus);\n resolve(this);\n }\n },\n onScheduleReconnect: (info: { delay: number }) => {\n if (responseStatus && this.isStatusFatal(responseStatus)) {\n this.close();\n reject(this.prepareFatalError(responseStatus, errorBody));\n } else {\n this.logger.warn(\n `[EventSourceTransport] Scheduling reconnect in ${info.delay}. Response status: ${responseStatus}`,\n );\n }\n },\n onDisconnect: () => {\n this.logger.debug(\"[EventSourceTransport] Disconnected\");\n },\n });\n });\n return Promise.race([\n eventSourcePromise,\n new Promise<this>((resolve) => {\n setTimeout(() => resolve(this), timeout);\n }),\n ]);\n }\n\n private dispatchMessage(data: string) {\n try {\n const json = JSON.parse(data);\n this.eventEmitter.emit(\"configBundleReceived\", json as ConfigBundle);\n } catch (error) {\n this.logger.error(\"[EventSourceTransport] Error parsing and dispatching config data update: \", error);\n }\n }\n\n private prepareFatalError(\n responseStatus: number,\n errorBody: string | undefined,\n ): ConfigDirectorConnectionError {\n const status = responseStatus ?? 0;\n const headline = `Connection failed with status: ${responseStatus ?? \"unknown\"}`;\n const serverBody = (errorBody?.trim()?.length ?? 0) > 0 ? ` (${errorBody})` : \"\";\n const message = `${headline}${serverBody}. This is an unrecoverable error, will not attempt to reconnect.`;\n return new ConfigDirectorConnectionError(message, status);\n }\n\n private isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500;\n }\n\n public get isConnected(): boolean {\n return this.eventSource?.readyState == \"open\";\n }\n\n public on<TName extends keyof TransportEvents>(\n name: TName,\n handler: (payload: TransportEvents[TName]) => void,\n ): void {\n this.eventEmitter.on(name, handler);\n }\n\n public off<TName extends keyof TransportEvents>(\n name: TName,\n handler?: ((payload: TransportEvents[TName]) => void) | undefined,\n ): void {\n if (handler) {\n this.eventEmitter.off(name, handler);\n } else {\n this.eventEmitter.removeAllListeners(name);\n }\n }\n\n public clear(): void {\n this.eventEmitter.removeAllListeners();\n }\n\n public close() {\n this.eventSource?.close();\n }\n\n public dispose(): void {\n this.close();\n this.clear();\n }\n}\n","import { Operator } from \"./types\";\n\nexport const compareText = (value: string | null | undefined, operator: Operator, targetValues: string[]) => {\n switch (operator.toLowerCase()) {\n case \"=\":\n case \"equals\":\n return value == targetValues[0];\n case \"!=\":\n case \"does not equal\":\n return value != targetValues[0];\n case \"is one of\":\n return targetValues.includes(value as any);\n case \"is not one of\":\n return !targetValues.includes(value as any);\n case \"starts with any of\":\n return targetValues.findIndex((v) => value?.startsWith(v)) >= 0;\n case \"does not start with any of\":\n return targetValues.findIndex((v) => value?.startsWith(v)) < 0;\n case \"ends with any of\":\n return targetValues.findIndex((v) => value?.endsWith(v)) >= 0;\n case \"does not end with any of\":\n return targetValues.findIndex((v) => value?.endsWith(v)) < 0;\n case \"matches regex\":\n return matchesRegex(targetValues[0], value);\n case \"does not match regex\":\n return !matchesRegex(targetValues[0], value);\n default:\n return false;\n }\n};\n\nconst matchesRegex = (regexString: string, value: string | null | undefined) => {\n try {\n return new RegExp(regexString).test(value as any);\n } catch {\n return false;\n }\n};\n","import { Operator } from \"./types\";\n\nexport const compareNumeric = (\n givenValue: number | bigint | string | boolean | null | undefined,\n operator: Operator,\n targetValues: string[],\n) => {\n if (typeof givenValue == \"boolean\") {\n return false;\n }\n const lowercaseOperator = operator.toLowerCase();\n if (givenValue == null) {\n return lowercaseOperator == \"!=\" || lowercaseOperator == \"does not equal\";\n }\n\n const { value, target } = parseValueAndTarget(givenValue, targetValues[0]);\n\n switch (lowercaseOperator) {\n case \"=\":\n case \"equals\":\n return value == target;\n case \"!=\":\n case \"does not equal\":\n return value != target;\n case \"<\":\n return value < target;\n case \"<=\":\n return value <= target;\n case \">\":\n return value > target;\n case \">=\":\n return value >= target;\n default:\n return false;\n }\n};\n\nconst parseValueAndTarget = (value: number | bigint | string, targetValue: string) => {\n if (typeof value == \"number\") {\n return { value, target: parseAsNumber(targetValue) };\n } else if (typeof value == \"bigint\") {\n return { value, target: BigInt(targetValue) };\n } else {\n return { value: parseAsNumber(value), target: parseAsNumber(targetValue) };\n }\n};\n\nconst parseAsNumber = (value: number | bigint | string): number => {\n const n = Number.parseFloat(value.toString());\n if (isNaN(n) || !isFinite(n)) {\n throw new TypeError(`Invalid numeric value: ${value}`);\n }\n return n;\n};\n","import { Operator } from \"./types\";\n\nexport const compareDate = (value: string | null | undefined, operator: Operator, targetValues: string[]) => {\n if (value == null) {\n return false;\n }\n\n try {\n const dateValue = new Date(value);\n const dateTarget = new Date(targetValues[0]);\n switch (operator) {\n case \"is after\":\n return dateValue > dateTarget;\n case \"is before\":\n return dateValue < dateTarget;\n default:\n return false;\n }\n } catch {\n return false;\n }\n};\n","import { Operator } from \"./types\";\nimport * as semver from \"semver\";\n\nexport const compareSemver = (value: string | null | undefined, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n if (value == null || value.trim().length == 0) {\n return lowercaseOperator == \"is not one of\";\n }\n\n const semverValue = semver.coerce(value) ?? value;\n const semverTargets = targetValues.map((v) => semver.coerce(v) ?? v);\n try {\n switch (lowercaseOperator) {\n case \"=\":\n return safeEquals(semverValue, semverTargets[0]);\n case \"<\":\n return semver.lt(semverValue, semverTargets[0]);\n case \"<=\":\n return semver.lte(semverValue, semverTargets[0]);\n case \">\":\n return semver.gt(semverValue, semverTargets[0]);\n case \">=\":\n return semver.gte(semverValue, semverTargets[0]);\n case \"is one of\":\n return semverTargets.findIndex((v) => safeEquals(semverValue, v)) >= 0;\n case \"is not one of\":\n return semverTargets.findIndex((v) => safeEquals(semverValue, v)) < 0;\n default:\n return false;\n }\n } catch {\n return false;\n }\n};\n\nconst safeEquals = (v1: string | semver.SemVer, v2: string | semver.SemVer): boolean => {\n try {\n return semver.eq(v1, v2);\n } catch {\n return false;\n }\n};\n","import { Operator } from \"./types\";\n\nexport const compareArray = (givenValue: any, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n if (!Array.isArray(givenValue)) {\n return lowercaseOperator == \"does not contain any of\";\n }\n\n switch (lowercaseOperator) {\n case \"contains any of\":\n return givenValue.some((v) => targetValues.includes(v));\n case \"does not contain any of\":\n return !givenValue.some((v) => targetValues.includes(v));\n default:\n return false;\n }\n};\n","import { Condition, EvaluationContext } from \"./types\";\nimport { findByPointer } from \"@jsonjoy.com/json-pointer\";\nimport { compareText } from \"./text-comparison\";\nimport { compareNumeric } from \"./numeric-comparison\";\nimport { compareDate } from \"./date-comparison\";\nimport { compareSemver } from \"./semver-comparison\";\nimport { compareArray } from \"./array-comparison\";\n\nexport class ConditionEvaluator {\n public evaluate(condition: Condition, context?: EvaluationContext): boolean {\n if (!this.isConditionValidForComparison(condition)) {\n return false;\n }\n\n switch (condition.attribute) {\n case \"identifier\":\n return this.compare(context?.context?.id, condition);\n case \"name\":\n return this.compare(context?.context?.name, condition);\n case \"appName\":\n return this.compare(context?.metadata?.appName, condition);\n case \"appVersion\":\n return this.compare(context?.metadata?.appVersion, condition);\n case \"traits\":\n if (condition.trait) {\n const value = this.findTraitValue(context?.context?.traits, condition.trait);\n return this.compare(value, condition);\n }\n }\n\n return false;\n }\n\n private compare(\n value: any,\n condition: Condition,\n ): boolean {\n switch (condition.targetType) {\n case \"text\":\n return compareText(value?.toString(), condition.operator, condition.targetValues);\n case \"number\":\n return compareNumeric(value, condition.operator, condition.targetValues);\n case \"datetime\":\n return compareDate(value?.toString(), condition.operator, condition.targetValues);\n case \"semver\":\n return compareSemver(value?.toString(), condition.operator, condition.targetValues);\n case \"array\":\n return compareArray(value, condition.operator, condition.targetValues);\n }\n return false;\n }\n\n private findTraitValue(traits: Record<string, unknown> | undefined, path: string) {\n try {\n const reference = findByPointer(path, traits);\n return reference.val;\n } catch {\n return undefined;\n }\n }\n\n private isConditionValidForComparison(condition: Condition): boolean {\n return Array.isArray(condition.targetValues) && condition.targetValues.length > 0;\n }\n}\n","import { rapidhash } from \"rapidhash-js\";\n\nconst SEED = 0x397832987n;\n\nexport const assignPercentage = (data: {configId: string, contextIdentifier: string}): number => {\n const value = `${data.contextIdentifier}-${data.configId}`;\n\n return Number(rapidhash(value, { seed: SEED }) % 1_000n) / 10.0;\n};\n","import { ConditionEvaluator } from \"./ConditionEvaluator\";\nimport { assignPercentage } from \"./percent-hashing\";\nimport {\n ConditionalRule,\n Config,\n EvaluationContext,\n ConfigState,\n Percentage,\n PercentageRule,\n Rule,\n Condition,\n ConfigDirectorLogger,\n} from \"./types\";\n\ntype RuleSuccess = {\n success: true;\n value: string;\n};\n\ntype RuleFailure = {\n success: false;\n};\n\ntype RuleEvaluationResult = RuleSuccess | RuleFailure;\n\nexport class ConfigEvaluator {\n private readonly conditionEvaluator = new ConditionEvaluator();\n\n constructor(private readonly logger: ConfigDirectorLogger) {\n if (!logger || !logger.warn || !logger.error || !logger.info) {\n throw new TypeError(\"The provided logger is not a valid 'ConfigDirectorLogger'\");\n }\n\n this.logger = logger;\n }\n\n public evaluate(config: Config, context?: EvaluationContext): ConfigState {\n return {\n id: config.id,\n key: config.key,\n type: config.type,\n value: this.getConfigValue(config, context),\n };\n }\n\n private getConfigValue(config: Config, context?: EvaluationContext) {\n const rules = [...(config.target?.rules ?? [])].sort(\n (a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER),\n );\n for (const rule of rules) {\n const result = this.evaluateRule(rule, config, context);\n if (result.success) {\n return result.value;\n }\n }\n return config.target?.defaultValue;\n }\n\n private evaluateRule(rule: Rule, config: Config, context?: EvaluationContext): RuleEvaluationResult {\n try {\n if (rule.type == \"percentage\") {\n return this.evaluatePercentageRule(rule, config, context);\n } else if (rule.type == \"conditional\") {\n return this.evaluateConditionalRule(rule, config, context);\n }\n } catch (error) {\n this.logger.warn(`There was an error while evaluating a targeting rule '${rule?.id}' for '${config?.key}'. The rule will be disregarded.`, {\n error,\n configKey: config?.key,\n ruleId: rule?.id,\n });\n }\n\n return { success: false };\n }\n\n private evaluatePercentageRule(\n rule: PercentageRule,\n config: Config,\n context?: EvaluationContext,\n ): RuleEvaluationResult {\n return this.evaluatePercentage(rule.percentages ?? [], config, context);\n }\n\n private evaluatePercentage(\n percentages: Percentage[],\n config: Config,\n context?: EvaluationContext,\n ): RuleEvaluationResult {\n const assignedPercentage = assignPercentage({\n configId: config.id,\n contextIdentifier: context?.context?.id ?? crypto.randomUUID(),\n });\n let index = 0;\n let sum = 0.0;\n let bucket: Percentage | undefined = undefined;\n while (!bucket && index < percentages.length) {\n if (assignedPercentage <= percentages[index].percentage + sum) {\n bucket = percentages[index];\n }\n\n sum += percentages[index].percentage;\n index += 1;\n }\n\n if (bucket?.value != null) {\n return { success: true, value: bucket.value.toString() };\n }\n return { success: false };\n }\n\n private evaluateConditionalRule(\n rule: ConditionalRule,\n config: Config,\n context?: EvaluationContext,\n ): RuleEvaluationResult {\n const conditions = rule.conditions ?? [];\n let index = 0;\n let condition: Condition | undefined = undefined;\n while (!condition && index < conditions.length) {\n if (this.conditionEvaluator.evaluate(conditions[index], context)) {\n condition = conditions[index];\n }\n index += 1;\n }\n\n if (condition && rule.target == \"value\" && rule.value != null) {\n return { success: true, value: rule.value.toString() };\n } else if (condition && rule.target == \"percentage\") {\n return this.evaluatePercentage(rule.percentages ?? [], config, context);\n }\n return { success: false };\n }\n}\n","import type { ConfigState, ConfigValueType, EvaluationReason } from \"./types\";\n\ntype NativeType =\n | \"string\"\n | \"number\"\n | \"bigint\"\n | \"boolean\"\n | \"symbol\"\n | \"undefined\"\n | \"object\"\n | \"function\";\n\ntype ParseResult<T extends ConfigValueType> = {\n parsedValue: T;\n requestedType: NativeType | string;\n usedDefault: boolean;\n reason: EvaluationReason;\n};\n\nexport const getRequestedType = <T extends ConfigValueType>(defaultValue: T): string => {\n const baseType = typeof defaultValue;\n if (baseType === \"object\") {\n try {\n return (defaultValue as object).constructor?.name ?? baseType;\n } catch {\n return baseType;\n }\n } else if (baseType === \"function\") {\n try {\n const functionName = (defaultValue as any).name;\n return functionName ? `function: ${functionName}` : baseType;\n } catch {\n return baseType;\n }\n }\n return baseType;\n};\n\nconst isNumericNativeType = (requestedType: NativeType): boolean => {\n return requestedType === \"number\" || requestedType === \"bigint\";\n};\n\nexport const parseConfigValue = <T extends ConfigValueType>(\n configState: ConfigState,\n defaultValue: T,\n): ParseResult<T> => {\n const value = configState.value;\n const requestedType = getRequestedType(defaultValue);\n\n if (!value) {\n return {\n parsedValue: defaultValue as T,\n requestedType,\n usedDefault: true,\n reason: \"value-missing\",\n };\n }\n\n if (typeof defaultValue === \"string\") {\n return {\n parsedValue: value as T,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n }\n\n if (typeof defaultValue === \"boolean\" && configState.type === \"boolean\") {\n const boolValue = parseConfigBoolean(value);\n const hasBoolean = typeof boolValue === \"boolean\";\n return {\n parsedValue: (hasBoolean ? boolValue : defaultValue) as T,\n requestedType,\n usedDefault: !hasBoolean,\n reason: hasBoolean ? \"found-match\" : \"invalid-boolean\",\n };\n }\n\n if (isNumericNativeType(typeof defaultValue) && configState.type === \"integer\") {\n const numValue = parseConfigInteger(value);\n const hasNumber = typeof numValue === \"number\";\n return {\n parsedValue: (hasNumber ? numValue : defaultValue) as T,\n requestedType,\n usedDefault: !hasNumber,\n reason: hasNumber ? \"found-match\" : \"invalid-number\",\n };\n }\n if (isNumericNativeType(typeof defaultValue) && (configState.type === \"float\" || configState.type === \"enum\" )) {\n const numValue = parseConfigFloat(value);\n const hasNumber = typeof numValue === \"number\";\n return {\n parsedValue: (hasNumber ? numValue : defaultValue) as T,\n requestedType,\n usedDefault: !hasNumber,\n reason: hasNumber ? \"found-match\" : \"invalid-number\",\n };\n }\n\n return {\n parsedValue: value as T,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n};\n\nconst parseConfigBoolean = (value: string): boolean | undefined => {\n if (!value) {\n return;\n }\n const lowerValue = value.toLowerCase();\n if (lowerValue != \"true\" && lowerValue != \"false\") {\n return;\n }\n return lowerValue === \"true\";\n};\n\nconst parseConfigInteger = (value: string): number | undefined => {\n if (!value) {\n return;\n }\n const num = Number.parseInt(value);\n if (isNaN(num) || !isFinite(num)) {\n return;\n }\n return num;\n};\n\nconst parseConfigFloat = (value: string): number | undefined => {\n if (!value) {\n return;\n }\n const num = Number.parseFloat(value);\n if (isNaN(num) || !isFinite(num)) {\n return;\n }\n return num;\n};\n","import { ConfigDirectorLogger, ConfigDirectorLogMessageDecorator, ConfigDirectorLoggingLevel } from \"./types\";\n\nconst LEVELS: Record<ConfigDirectorLoggingLevel, number> = {\n off: -1,\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\nconst buildDateFormatter = () => {\n const baseOptions: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZoneName: \"short\",\n };\n\n try {\n return new Intl.DateTimeFormat(\n undefined,\n { ...baseOptions, fractionalSecondDigits: 3 } as Intl.DateTimeFormatOptions,\n );\n } catch {\n return new Intl.DateTimeFormat(undefined, baseOptions);\n }\n};\n\nexport class DefaultConsoleLogger implements ConfigDirectorLogger {\n private readonly dateFormatter;\n\n constructor(private readonly level: ConfigDirectorLoggingLevel, private readonly decorator: ConfigDirectorLogMessageDecorator) {\n this.level = level;\n this.decorator = decorator;\n this.dateFormatter = buildDateFormatter();\n }\n\n debug(message: string, ...args: any): void {\n this.log(console.debug, \"debug\", message, ...args);\n }\n\n info(message: string, ...args: any): void {\n this.log(console.info, \"info\", message, ...args);\n }\n\n warn(message: string, ...args: any): void {\n this.log(console.warn, \"warn\", message, ...args);\n }\n\n error(message: string, ...args: any): void {\n this.log(console.error, \"error\", message, ...args);\n }\n\n private log(loggerFunction: (...args: any) => void, level: ConfigDirectorLoggingLevel, message: string, ...args: any) {\n if (LEVELS[this.level] >= LEVELS[level]) {\n loggerFunction(`[${this.dateFormatter.format(new Date())}] ${this.decorator?.decorateMessage(message)}`, ...args);\n }\n }\n}\n","import { DefaultConsoleLogger } from \"@shared/logger\";\nimport { ConfigDirectorLoggingLevel, ConfigDirectorLogMessageDecorator } from \"@shared/types\";\n\nclass LogMessageDecorator implements ConfigDirectorLogMessageDecorator {\n decorateMessage(message: string): string {\n return `[ConfigDirector:js-server-sdk] ${message}`;\n }\n}\n\nexport const createDefaultLogger = (\n level?: ConfigDirectorLoggingLevel,\n messageDecorator?: ConfigDirectorLogMessageDecorator,\n) => {\n return new DefaultConsoleLogger(level ?? \"warn\", messageDecorator ?? new LogMessageDecorator());\n};\n","import { DroppedEvents } from \"./types\";\n\nexport const djb2Hash = (data: string): string => {\n const hash = djb2(new TextEncoder().encode(data));\n return hash.toString(16).padStart(8, \"0\");\n};\n\nconst djb2 = (bytes: Uint8Array): number => {\n let hash = 5381;\n for (let i = 0; i < bytes.length; i++) {\n hash = (hash << 5) + hash + bytes[i];\n hash = hash >>> 0;\n }\n return hash;\n};\n\nexport const isEventListEmpty = <T extends Record<string | symbol, any[]>>(eventList: T): boolean => {\n const keys = Object.keys(eventList);\n if (keys.length == 0) {\n return true;\n }\n\n for (const key of keys) {\n if ((eventList[key]?.length ?? 0) > 0) {\n return false;\n }\n }\n\n return true;\n};\n\nexport const isDroppedEventsEmpty = (droppedEvents?: DroppedEvents): boolean => {\n if (!droppedEvents) {\n return true;\n }\n const keys = Object.keys(droppedEvents);\n if (keys.length == 0) {\n return true;\n }\n\n for (const key of keys) {\n if ((droppedEvents[key] ?? 0) > 0) {\n return false;\n }\n }\n\n return true;\n};\n","import { ConfigDirectorLogger } from \"./types\";\n\nexport const fetchWithTimeout = async (\n timeout: number,\n resource: string | URL | Request,\n options: RequestInit | undefined,\n logger: ConfigDirectorLogger,\n) => {\n const abortController = new AbortController();\n const abortTimeoutId = setTimeout(() => {\n logger.debug(\"[fetchWithTimeout] Reached timeout, aborting request\");\n abortController.abort();\n }, timeout);\n\n try {\n const response = await fetch(resource, {\n ...options,\n signal: abortController.signal,\n });\n clearTimeout(abortTimeoutId);\n return response;\n } catch (error) {\n logger.warn(\"[fetchWithTimeout] Fetch error: \", error);\n clearTimeout(abortTimeoutId);\n throw error;\n }\n};\n","import { isDroppedEventsEmpty, isEventListEmpty } from \"@shared/telemetry/utils\";\nimport { ConfigDirectorLogger } from \"../types\";\nimport { EventReport, EventReporter, EventReporterPayload, ReporterResponse } from \"./types\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\n\nexport type EventReporterOptions = {\n sdkKey: string;\n logger: ConfigDirectorLogger;\n baseUrl: URL;\n};\n\nexport class ServerEventReporter implements EventReporter {\n private readonly sdkKey: string;\n private readonly logger: ConfigDirectorLogger;\n private readonly url: URL;\n private executeRequests = true;\n\n constructor(options: EventReporterOptions) {\n this.sdkKey = options.sdkKey;\n this.logger = options.logger;\n this.url = new URL(\"server/telemetry/v1\", options.baseUrl);\n }\n\n public async report({\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n }: EventReporterPayload): Promise<ReporterResponse> {\n if (!this.executeRequests) {\n return { success: false, fatalError: true };\n }\n\n const eventReport: EventReport = {\n serverSdkKey: this.sdkKey,\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n };\n if (this.isReportEmpty(eventReport)) {\n return { success: true, fatalError: false };\n }\n\n const response = await this.sendReport(eventReport);\n if (response.fatalError) {\n this.executeRequests = false;\n }\n return response;\n }\n\n private isReportEmpty(eventReport: EventReport) {\n return (\n isEventListEmpty(eventReport.discreteEvents) &&\n isEventListEmpty(eventReport.aggregatedEvents) &&\n isDroppedEventsEmpty(eventReport.droppedEvents)\n );\n }\n\n private async sendReport(eventReport: EventReport): Promise<ReporterResponse> {\n try {\n const response = await fetchWithTimeout(\n 5_000,\n this.url,\n {\n method: \"POST\",\n body: JSON.stringify(eventReport),\n },\n this.logger,\n );\n\n const isFatalError = this.isStatusFatal(response.status);\n if (isFatalError) {\n this.logger.warn(\n `[EventReporter] Received a fatal status response (${response.status}) from the telemetry endpoint. No more telemetry data will be sent.`,\n );\n }\n return { success: response.ok, fatalError: isFatalError };\n } catch (error) {\n this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${error}`, { error });\n return { success: false, fatalError: false };\n }\n }\n\n private isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500;\n }\n}\n","import { AggregatedEvent, EventQueueSnapshot, ReportableEvent } from \"./types\";\n\nexport class EventAggregator {\n public aggregate<T extends ReportableEvent>(snapshot: EventQueueSnapshot<T>): AggregatedEvent<T>[] {\n if (snapshot.events.length == 0) {\n return [];\n }\n\n const map = new Map<string, { event: T; count: number }>();\n for (const event of snapshot.events) {\n const serializedEvent = JSON.stringify(event);\n const count = map.get(serializedEvent)?.count || 0;\n map.set(serializedEvent, { event, count: count + 1 });\n }\n return Array.from(map).map(([, v]) => {\n return {\n startTime: snapshot.startTime,\n endTime: snapshot.endTime,\n count: v.count,\n event: v.event,\n };\n });\n }\n}\n","import { EventQueueSnapshot, ReportableEvent } from \"./types\";\n\nexport class EventQueue<T extends ReportableEvent> {\n private readonly limit: number;\n private startTime?: Date;\n private _events: T[] = [];\n private _droppedEventCount = 0;\n\n constructor(limit?: number) {\n this.limit = limit ?? 1_000;\n }\n\n public get events(): T[] {\n return this._events;\n }\n\n public get reachedLimit(): boolean {\n return this._events.length >= this.limit;\n }\n\n public get droppedEventCount(): number {\n return this._droppedEventCount;\n }\n\n public push(...newEvents: T[]) {\n if (!this.startTime) {\n this.startTime = new Date();\n }\n\n const newEventsCountToDrop = Math.max(0, newEvents.length - this.limit);\n const eventsToPush = newEvents.slice(newEventsCountToDrop, newEvents.length);\n this._droppedEventCount += newEventsCountToDrop;\n if (this._events.length + eventsToPush.length > this.limit) {\n const eventCountToDrop = this._events.length + eventsToPush.length - this.limit;\n this._events.splice(0, eventCountToDrop);\n this._droppedEventCount += eventCountToDrop;\n }\n return this._events.push(...eventsToPush);\n }\n\n public takeSnapshot(): EventQueueSnapshot<T> {\n const endTime = new Date();\n const startTime = this.startTime ?? endTime;\n const eventsSnapshot = this._events.splice(0);\n const droppedCount = this._droppedEventCount;\n this.startTime = undefined;\n this._droppedEventCount = 0;\n return {\n startTime,\n endTime,\n events: eventsSnapshot,\n droppedCount,\n };\n }\n\n public clear() {\n this._events = [];\n this.startTime = undefined;\n this._droppedEventCount = 0;\n }\n}\n","import { ConfigDirectorContext, ConfigDirectorLogger, ConfigType, ConfigValueType } from \"../types\";\nimport { EventAggregator } from \"./EventAggregator\";\nimport { EventQueue } from \"./EventQueue\";\nimport { djb2Hash } from \"./utils\";\nimport { EventReporter, ReportableEvent, ReporterResponse } from \"./types\";\n\nconst CONFIG_VALUE_MAX_LENGTH = 500;\n\nexport type TelemetryEventCollectorOptions = {\n sdkKey: string;\n logger: ConfigDirectorLogger;\n baseUrl: URL;\n flushIntervalDelay?: number;\n initialFlushIntervalDelay?: number;\n};\n\nexport abstract class TelemetryEventCollector<T extends ReportableEvent> {\n protected readonly logger: ConfigDirectorLogger;\n protected abstract readonly reporter: EventReporter | undefined;\n protected evaluationEventQueue: EventQueue<T> = new EventQueue();\n protected readonly aggregator: EventAggregator = new EventAggregator();\n protected flushIntervalDelay: number;\n protected flushTimeout: ReturnType<typeof setTimeout>;\n protected collectEvents = true;\n protected abstract _context?: ConfigDirectorContext;\n\n constructor(options: TelemetryEventCollectorOptions) {\n this.logger = options.logger;\n this.flushIntervalDelay = options.flushIntervalDelay ?? 30_000;\n const initialDelay = options.initialFlushIntervalDelay ?? 5_000;\n this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);\n }\n\n public abstract evaluatedConfig(event: T): void;\n\n protected sanitizeValue<T extends ConfigValueType>(value: T, type?: ConfigType): string {\n if (type === \"json\") {\n try {\n const json = JSON.stringify(value);\n return djb2Hash(json);\n } catch {\n return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);\n }\n }\n\n return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);\n }\n\n protected async flushAndScheduleNext() {\n const response = await this.flush();\n if (response.fatalError) {\n this.collectEvents = false;\n this.close();\n this.logger.warn(\n \"[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.\",\n );\n } else {\n this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);\n }\n }\n\n protected async flush(): Promise<ReporterResponse> {\n if (!this.reporter) {\n return { success: false, fatalError: false };\n }\n const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();\n const response = await this.reporter?.report({\n context: this._context,\n discreteEvents: {},\n aggregatedEvents: {\n evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),\n },\n droppedEvents: {\n evaluatedConfig: evaluationSnapshot.droppedCount,\n },\n });\n return response;\n }\n\n protected cancelScheduledFlush() {\n clearTimeout(this.flushTimeout);\n }\n\n public close() {\n this.collectEvents = false;\n clearTimeout(this.flushTimeout);\n this.flush();\n this.evaluationEventQueue.clear();\n }\n\n public dispose() {\n this.close();\n }\n}\n","import { ConfigDirectorContext, ConfigValueType } from \"../types\";\nimport { ServerEventReporter } from \"./ServerEventReporter\";\nimport { EvaluatedConfigEvent } from \"./telemetry-events\";\nimport { EventReporter } from \"./types\";\nimport { TelemetryEventCollector, TelemetryEventCollectorOptions } from \"@shared/telemetry/TelemetryEventCollector\";\n\nexport class ServerTelemetryEventCollector extends TelemetryEventCollector<EvaluatedConfigEvent<string>> {\n protected readonly reporter: EventReporter;\n protected _context?: ConfigDirectorContext;\n\n constructor(options: TelemetryEventCollectorOptions) {\n super(options);\n this.reporter = new ServerEventReporter(options);\n }\n\n public evaluatedConfig<T extends ConfigValueType>(event: EvaluatedConfigEvent<T>): void {\n if (!this.collectEvents) {\n return;\n }\n\n this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(event));\n }\n\n private sanitizeEvaluatedConfigEvent<T extends ConfigValueType>(\n event: EvaluatedConfigEvent<T>,\n ): EvaluatedConfigEvent<string> {\n return {\n key: event.key,\n type: event.type,\n defaultValue: this.sanitizeValue(event.defaultValue, event.type),\n requestedType: event.requestedType,\n evaluatedValue: this.sanitizeValue(event.evaluatedValue, event.type),\n usedDefault: event.usedDefault,\n evaluationReason: event.evaluationReason,\n };\n }\n}\n","import { StreamingTransport } from \"./StreamingTransport\";\nimport { ConfigEvaluator } from \"@config-evaluator/ConfigEvaluator\";\nimport { getRequestedType, parseConfigValue } from \"@shared/value-parser\";\nimport {\n ConfigDirectorContext,\n ConfigDirectorClientOptions,\n ConfigDirectorClient,\n ClientEvents,\n WatchHandler,\n ConfigValueType,\n ConfigDirectorLogger,\n Transport,\n IdentifyingSdkOptions,\n ConfigBundle,\n ConfigDefinition,\n} from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\nimport { ConfigDirectorValidationError } from \"@shared/errors\";\nimport EventEmitter from \"node:events\";\nimport { ConfigDirectorMetaContext } from \"@shared/types\";\nimport { ServerTelemetryEventCollector } from \"./telemetry\";\n\nconst defaultBaseUrl = new URL(\"https://server-sdk-api.configdirector.com\");\n\ntype WatchHandlerWithOptions<T extends ConfigValueType> = {\n handler: WatchHandler<T>;\n defaultValue: T;\n requestedType: string;\n context?: ConfigDirectorContext;\n};\n\nexport class DefaultConfigDirectorClient implements ConfigDirectorClient {\n private logger: ConfigDirectorLogger;\n private usageEventCollector: ServerTelemetryEventCollector;\n private configSet: ConfigBundle | undefined;\n private handlersMap: Map<string, WatchHandlerWithOptions<any>[]> = new Map();\n private transport: Transport;\n private eventEmitter = new EventEmitter();\n private timeout: number;\n private ready = false;\n private readyPromise: Promise<void> | undefined;\n private readyResolve: (() => void) | undefined;\n private streaming: boolean;\n private configEvaluator: ConfigEvaluator;\n private metaContext: ConfigDirectorMetaContext;\n\n constructor(\n serverSdkKey: string,\n sdkOptions: IdentifyingSdkOptions,\n clientOptions?: ConfigDirectorClientOptions,\n ) {\n this.logger = clientOptions?.logger ?? createDefaultLogger();\n this.timeout = clientOptions?.connection?.timeout ?? 3_000;\n this.metaContext = clientOptions?.metadata ?? {};\n this.configEvaluator = new ConfigEvaluator(this.logger);\n const baseUrl = this.parseUrl(clientOptions?.connection?.url) ?? defaultBaseUrl;\n this.streaming = clientOptions?.connection?.streaming === false ? false : true;\n const transportConstructor = StreamingTransport;\n this.usageEventCollector = new ServerTelemetryEventCollector({\n sdkKey: serverSdkKey,\n logger: this.logger,\n baseUrl,\n });\n this.transport = new transportConstructor({\n serverSdkKey,\n baseUrl,\n metaContext: {\n ...clientOptions?.metadata,\n sdkName: sdkOptions.sdkName,\n sdkVersion: sdkOptions.sdkVersion,\n },\n logger: this.logger,\n });\n\n this.transport.on(\"configBundleReceived\", (configBundle: ConfigBundle) => {\n this.readyResolve?.();\n const configKeys = Object.keys(configBundle.configs);\n if (!this.configSet || configBundle.kind == \"full\") {\n this.configSet = configBundle;\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configBundle.configs);\n } else {\n this.configSet.configs = {\n ...this.configSet.configs,\n ...configBundle.configs,\n };\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configBundle.configs);\n }\n this.logger.debug(\"[ConfigDirectorClient] ConfigSet updated from server:\", { keys: configKeys });\n });\n }\n\n public async initialize() {\n try {\n this.ready = false;\n this.readyPromise = new Promise<void>((resolve) => {\n this.readyResolve = resolve;\n }).then(() => {\n this.ready = true;\n this.eventEmitter.emit(\"clientReady\");\n this.logger.debug(\"[ConfigDirectorClient] Received initial payload from the server, client is ready\");\n });\n const startTime = new Date().getTime();\n await this.transport.connect(this.timeout);\n const elapsedTime = new Date().getTime() - startTime;\n const remainingTimeout = this.timeout - elapsedTime;\n if (remainingTimeout > 0) {\n await Promise.race([\n this.readyPromise,\n new Promise<void>((resolve) => {\n setTimeout(() => resolve(), remainingTimeout);\n }),\n ]);\n }\n if (!this.ready) {\n const warningDetails = this.streaming\n ? \"The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.\"\n : \"Since the client was configured without streaming, configs may not update and always return the default value.\";\n this.logger.warn(\n `[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${warningDetails}`,\n );\n }\n } catch (error) {\n this.logger.error(\"[ConfigDirectorClient] An error occurred during initialization: \", error);\n }\n }\n\n private updateWatchers(configsMap: Record<string, ConfigDefinition>) {\n Object.values(configsMap).forEach((v) => this.updateWatchersForConfig(v as ConfigDefinition));\n }\n\n private updateWatchersForConfig(configDefinition: ConfigDefinition) {\n this.handlersMap.get(configDefinition.key)?.forEach((h) => {\n const value = this.getValueFromConfigDefinition(\n configDefinition.key,\n configDefinition,\n h.defaultValue,\n h.context,\n );\n h.handler(value);\n });\n }\n\n public watch<T extends ConfigValueType>(\n configKey: string,\n defaultValue: T,\n callback: WatchHandler<T>,\n context?: ConfigDirectorContext,\n ) {\n this.validateDefaultValue(defaultValue);\n\n const handlers = this.handlersMap.get(configKey);\n const handlerWithOptions = {\n handler: callback,\n defaultValue,\n requestedType: typeof defaultValue,\n context,\n };\n if (handlers) {\n handlers.push(handlerWithOptions);\n } else {\n this.handlersMap.set(configKey, [handlerWithOptions]);\n }\n\n return () => this.unwatch(configKey, callback);\n }\n\n public unwatch<T extends ConfigValueType>(configKey: string, callback?: WatchHandler<T>) {\n const handlers = this.handlersMap.get(configKey);\n if (!handlers) {\n return;\n }\n\n if (callback) {\n const index = handlers.findIndex((h) => h.handler == callback);\n if (index >= 0) {\n handlers?.splice(index, 1);\n }\n } else {\n handlers.splice(0);\n }\n }\n\n public getValue<T extends ConfigValueType>(\n configKey: string,\n defaultValue: T,\n context?: ConfigDirectorContext,\n ): T {\n this.validateDefaultValue(defaultValue);\n\n const configDefinition = this.configSet?.configs[configKey];\n return this.getValueFromConfigDefinition(configKey, configDefinition, defaultValue, context);\n }\n\n private getValueFromConfigDefinition<T extends ConfigValueType>(\n configKey: string,\n configDefinition: ConfigDefinition | undefined,\n defaultValue: T,\n context?: ConfigDirectorContext,\n ): T {\n if (configDefinition) {\n const configState = this.configEvaluator.evaluate(configDefinition, {\n context,\n metadata: this.metaContext,\n });\n const parseResult = parseConfigValue<T>(configState, defaultValue);\n this.usageEventCollector.evaluatedConfig({\n contextId: context?.id,\n key: configKey,\n defaultValue: defaultValue,\n requestedType: parseResult.requestedType,\n evaluatedValue: parseResult.parsedValue,\n usedDefault: parseResult.usedDefault,\n evaluationReason: parseResult.reason,\n });\n this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);\n return parseResult.parsedValue;\n } else {\n this.logger.debug(\n `[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`,\n );\n this.usageEventCollector.evaluatedConfig({\n contextId: context?.id,\n key: configKey,\n defaultValue: defaultValue,\n requestedType: getRequestedType(defaultValue),\n evaluatedValue: defaultValue,\n usedDefault: true,\n evaluationReason: \"config-state-missing\",\n });\n return defaultValue;\n }\n }\n\n private parseUrl(url: string | undefined): URL | undefined {\n if (!url) {\n return;\n }\n\n try {\n return new URL(url);\n } catch (error) {\n throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);\n }\n }\n\n private validateDefaultValue<T extends ConfigValueType>(defaultValue: T) {\n if (defaultValue === undefined || defaultValue === null) {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config must be defined and non-null.\",\n );\n }\n\n if (typeof defaultValue === \"function\") {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config cannot be a function.\",\n );\n }\n }\n\n public get isReady(): boolean {\n return this.ready;\n }\n\n public on<T extends keyof ClientEvents>(eventName: T, handler: (args: ClientEvents[T]) => void): void {\n this.eventEmitter.on(eventName, handler);\n }\n\n public off<T extends keyof ClientEvents>(eventName: T, handler?: (args: ClientEvents[T]) => void) {\n if (handler) {\n this.eventEmitter.off(eventName, handler);\n } else {\n this.eventEmitter.removeAllListeners(eventName);\n }\n }\n\n public removeAllObservers() {\n this.logger.debug(\"[ConfigDirectorClient] removeAllObservers() has been called, removing all observers\");\n this.eventEmitter.removeAllListeners();\n this.handlersMap.clear();\n }\n\n public unwatchAll() {\n this.handlersMap.clear();\n }\n\n public closeConnection() {\n this.logger.debug(\n \"[ConfigDirectorClient] closeConnection() has been called, closing connection to server\",\n );\n this.transport.close();\n this.ready = false;\n }\n\n public dispose() {\n this.removeAllObservers();\n this.closeConnection();\n }\n}\n","import { ConfigDirectorLoggingLevel, ConfigDirectorLogMessageDecorator } from \"@shared/types\";\nimport { DefaultConfigDirectorClient } from \"./DefaultConfigDirectorClient\";\nimport { ConfigDirectorClient, ConfigDirectorClientOptions } from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\n\nexport const createClient = (\n serverSdkKey: string,\n clientOptions?: ConfigDirectorClientOptions,\n): ConfigDirectorClient => {\n return new DefaultConfigDirectorClient(\n serverSdkKey,\n { sdkName: \"js-server-sdk\", sdkVersion: \"__VERSION__\" },\n clientOptions,\n );\n};\n\nexport const createConsoleLogger = (\n level: ConfigDirectorLoggingLevel,\n messageDecorator?: ConfigDirectorLogMessageDecorator,\n) => {\n return createDefaultLogger(level, messageDecorator);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"configdirector-server.mjs","names":["EventEmitter"],"sources":["../../shared/src/errors.ts","../src/StreamingTransport.ts","../../config-evaluator/src/text-comparison.ts","../../config-evaluator/src/numeric-comparison.ts","../../config-evaluator/src/date-comparison.ts","../../config-evaluator/src/semver-comparison.ts","../../config-evaluator/src/array-comparison.ts","../../config-evaluator/src/ConditionEvaluator.ts","../../config-evaluator/src/percent-hashing.ts","../../config-evaluator/src/ConfigEvaluator.ts","../../shared/src/value-parser.ts","../../shared/src/logger.ts","../src/logger.ts","../../shared/src/telemetry/utils.ts","../../shared/src/fetchWithTimeout.ts","../src/telemetry/ServerEventReporter.ts","../../shared/src/telemetry/EventAggregator.ts","../../shared/src/telemetry/EventQueue.ts","../../shared/src/telemetry/TelemetryEventCollector.ts","../../shared/src/LimitedMap.ts","../src/telemetry/ServerTelemetryEventCollector.ts","../src/DefaultConfigDirectorClient.ts","../src/api.ts"],"sourcesContent":["export class ConfigDirectorConnectionError extends Error {\n public override readonly name: string = \"ConfigDirectorConnectionError\";\n public readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.status = status;\n\n Object.setPrototypeOf(this, ConfigDirectorConnectionError.prototype);\n }\n}\n\nexport class ConfigDirectorValidationError extends Error {\n public override readonly name: string = \"ConfigDirectorValidationError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ConfigDirectorValidationError.prototype);\n }\n}\n","import { createEventSource, type EventSourceClient } from \"eventsource-client\";\nimport type {\n ConfigBundle,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { ConfigDirectorConnectionError } from \"@shared/errors\";\nimport { EventEmitter } from \"node:events\";\n\nexport class StreamingTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventSource: EventSourceClient | undefined;\n private eventEmitter = new EventEmitter();\n private url: URL;\n\n constructor(private readonly options: TransportOptions) {\n this.options = options;\n this.logger = options.logger;\n this.url = new URL(\"server/sse/v1\", options.baseUrl);\n }\n\n public async connect(timeout: number): Promise<this> {\n if (this.eventSource) {\n this.close();\n }\n\n let responseStatus: number | undefined = undefined;\n let errorBody: string | undefined = undefined;\n\n const customFetch = async (url: string | URL | Request, init?: RequestInit | undefined) => {\n const response = await fetch(url, init);\n responseStatus = response.status;\n if (!response.ok) {\n errorBody = await response.text();\n }\n return response;\n };\n\n const eventSourcePromise = new Promise<this>((resolve, reject) => {\n this.eventSource = createEventSource({\n url: this.url,\n fetch: customFetch,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n metaContext: this.options.metaContext,\n serverSdkKey: this.options.serverSdkKey,\n }),\n\n onMessage: ({ data }) => {\n this.dispatchMessage(data);\n },\n onConnect: () => {\n if (responseStatus && this.isStatusFatal(responseStatus)) {\n this.close();\n reject(this.prepareFatalError(responseStatus, errorBody));\n } else {\n this.logger.debug(`[EventSourceTransport] Connected, status: ${responseStatus}`);\n resolve(this);\n }\n },\n onScheduleReconnect: (info: { delay: number }) => {\n if (responseStatus && this.isStatusFatal(responseStatus)) {\n this.close();\n reject(this.prepareFatalError(responseStatus, errorBody));\n } else {\n this.logger.warn(\n `[EventSourceTransport] Scheduling reconnect in ${info.delay}. Response status: ${responseStatus}`,\n );\n }\n },\n onDisconnect: () => {\n this.logger.debug(\"[EventSourceTransport] Disconnected\");\n },\n });\n });\n return Promise.race([\n eventSourcePromise,\n new Promise<this>((resolve) => {\n setTimeout(() => resolve(this), timeout);\n }),\n ]);\n }\n\n private dispatchMessage(data: string) {\n try {\n const json = JSON.parse(data);\n this.eventEmitter.emit(\"configBundleReceived\", json as ConfigBundle);\n } catch (error) {\n this.logger.error(\"[EventSourceTransport] Error parsing and dispatching config data update: \", error);\n }\n }\n\n private prepareFatalError(\n responseStatus: number,\n errorBody: string | undefined,\n ): ConfigDirectorConnectionError {\n const status = responseStatus ?? 0;\n const headline = `Connection failed with status: ${responseStatus ?? \"unknown\"}`;\n const serverBody = (errorBody?.trim()?.length ?? 0) > 0 ? ` (${errorBody})` : \"\";\n const message = `${headline}${serverBody}. This is an unrecoverable error, will not attempt to reconnect.`;\n return new ConfigDirectorConnectionError(message, status);\n }\n\n private isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500;\n }\n\n public get isConnected(): boolean {\n return this.eventSource?.readyState == \"open\";\n }\n\n public on<TName extends keyof TransportEvents>(\n name: TName,\n handler: (payload: TransportEvents[TName]) => void,\n ): void {\n this.eventEmitter.on(name, handler);\n }\n\n public off<TName extends keyof TransportEvents>(\n name: TName,\n handler?: ((payload: TransportEvents[TName]) => void) | undefined,\n ): void {\n if (handler) {\n this.eventEmitter.off(name, handler);\n } else {\n this.eventEmitter.removeAllListeners(name);\n }\n }\n\n public clear(): void {\n this.eventEmitter.removeAllListeners();\n }\n\n public close() {\n this.eventSource?.close();\n }\n\n public dispose(): void {\n this.close();\n this.clear();\n }\n}\n","import { Operator } from \"./types\";\n\nexport const compareText = (value: string | null | undefined, operator: Operator, targetValues: string[]) => {\n switch (operator.toLowerCase()) {\n case \"=\":\n case \"equals\":\n return value == targetValues[0];\n case \"!=\":\n case \"does not equal\":\n return value != targetValues[0];\n case \"is one of\":\n return targetValues.includes(value as any);\n case \"is not one of\":\n return !targetValues.includes(value as any);\n case \"starts with any of\":\n return targetValues.findIndex((v) => value?.startsWith(v)) >= 0;\n case \"does not start with any of\":\n return targetValues.findIndex((v) => value?.startsWith(v)) < 0;\n case \"ends with any of\":\n return targetValues.findIndex((v) => value?.endsWith(v)) >= 0;\n case \"does not end with any of\":\n return targetValues.findIndex((v) => value?.endsWith(v)) < 0;\n case \"matches regex\":\n return matchesRegex(targetValues[0], value);\n case \"does not match regex\":\n return !matchesRegex(targetValues[0], value);\n default:\n return false;\n }\n};\n\nconst matchesRegex = (regexString: string, value: string | null | undefined) => {\n try {\n return new RegExp(regexString).test(value as any);\n } catch {\n return false;\n }\n};\n","import { Operator } from \"./types\";\n\nexport const compareNumeric = (\n givenValue: number | bigint | string | boolean | null | undefined,\n operator: Operator,\n targetValues: string[],\n) => {\n if (typeof givenValue == \"boolean\") {\n return false;\n }\n const lowercaseOperator = operator.toLowerCase();\n if (givenValue == null) {\n return lowercaseOperator == \"!=\" || lowercaseOperator == \"does not equal\";\n }\n\n const { value, target } = parseValueAndTarget(givenValue, targetValues[0]);\n\n switch (lowercaseOperator) {\n case \"=\":\n case \"equals\":\n return value == target;\n case \"!=\":\n case \"does not equal\":\n return value != target;\n case \"<\":\n return value < target;\n case \"<=\":\n return value <= target;\n case \">\":\n return value > target;\n case \">=\":\n return value >= target;\n default:\n return false;\n }\n};\n\nconst parseValueAndTarget = (value: number | bigint | string, targetValue: string) => {\n if (typeof value == \"number\") {\n return { value, target: parseAsNumber(targetValue) };\n } else if (typeof value == \"bigint\") {\n return { value, target: BigInt(targetValue) };\n } else {\n return { value: parseAsNumber(value), target: parseAsNumber(targetValue) };\n }\n};\n\nconst parseAsNumber = (value: number | bigint | string): number => {\n const n = Number.parseFloat(value.toString());\n if (isNaN(n) || !isFinite(n)) {\n throw new TypeError(`Invalid numeric value: ${value}`);\n }\n return n;\n};\n","import { Operator } from \"./types\";\n\nexport const compareDate = (value: string | null | undefined, operator: Operator, targetValues: string[]) => {\n if (value == null) {\n return false;\n }\n\n try {\n const dateValue = new Date(value);\n const dateTarget = new Date(targetValues[0]);\n switch (operator) {\n case \"is after\":\n return dateValue > dateTarget;\n case \"is before\":\n return dateValue < dateTarget;\n default:\n return false;\n }\n } catch {\n return false;\n }\n};\n","import { Operator } from \"./types\";\nimport * as semver from \"semver\";\n\nexport const compareSemver = (value: string | null | undefined, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n if (value == null || value.trim().length == 0) {\n return lowercaseOperator == \"is not one of\";\n }\n\n const semverValue = semver.coerce(value) ?? value;\n const semverTargets = targetValues.map((v) => semver.coerce(v) ?? v);\n try {\n switch (lowercaseOperator) {\n case \"=\":\n return safeEquals(semverValue, semverTargets[0]);\n case \"<\":\n return semver.lt(semverValue, semverTargets[0]);\n case \"<=\":\n return semver.lte(semverValue, semverTargets[0]);\n case \">\":\n return semver.gt(semverValue, semverTargets[0]);\n case \">=\":\n return semver.gte(semverValue, semverTargets[0]);\n case \"is one of\":\n return semverTargets.findIndex((v) => safeEquals(semverValue, v)) >= 0;\n case \"is not one of\":\n return semverTargets.findIndex((v) => safeEquals(semverValue, v)) < 0;\n default:\n return false;\n }\n } catch {\n return false;\n }\n};\n\nconst safeEquals = (v1: string | semver.SemVer, v2: string | semver.SemVer): boolean => {\n try {\n return semver.eq(v1, v2);\n } catch {\n return false;\n }\n};\n","import { Operator } from \"./types\";\n\nexport const compareArray = (givenValue: any, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n if (!Array.isArray(givenValue)) {\n return lowercaseOperator == \"does not contain any of\";\n }\n\n switch (lowercaseOperator) {\n case \"contains any of\":\n return givenValue.some((v) => targetValues.includes(v));\n case \"does not contain any of\":\n return !givenValue.some((v) => targetValues.includes(v));\n default:\n return false;\n }\n};\n","import { Condition, EvaluationContext } from \"./types\";\nimport { findByPointer } from \"@jsonjoy.com/json-pointer\";\nimport { compareText } from \"./text-comparison\";\nimport { compareNumeric } from \"./numeric-comparison\";\nimport { compareDate } from \"./date-comparison\";\nimport { compareSemver } from \"./semver-comparison\";\nimport { compareArray } from \"./array-comparison\";\n\nexport class ConditionEvaluator {\n public evaluate(condition: Condition, context?: EvaluationContext): boolean {\n if (!this.isConditionValidForComparison(condition)) {\n return false;\n }\n\n switch (condition.attribute) {\n case \"identifier\":\n return this.compare(context?.context?.id, condition);\n case \"name\":\n return this.compare(context?.context?.name, condition);\n case \"appName\":\n return this.compare(context?.metadata?.appName, condition);\n case \"appVersion\":\n return this.compare(context?.metadata?.appVersion, condition);\n case \"traits\":\n if (condition.trait) {\n const value = this.findTraitValue(context?.context?.traits, condition.trait);\n return this.compare(value, condition);\n }\n }\n\n return false;\n }\n\n private compare(\n value: any,\n condition: Condition,\n ): boolean {\n switch (condition.targetType) {\n case \"text\":\n return compareText(value?.toString(), condition.operator, condition.targetValues);\n case \"number\":\n return compareNumeric(value, condition.operator, condition.targetValues);\n case \"datetime\":\n return compareDate(value?.toString(), condition.operator, condition.targetValues);\n case \"semver\":\n return compareSemver(value?.toString(), condition.operator, condition.targetValues);\n case \"array\":\n return compareArray(value, condition.operator, condition.targetValues);\n }\n return false;\n }\n\n private findTraitValue(traits: Record<string, unknown> | undefined, path: string) {\n try {\n const reference = findByPointer(path, traits);\n return reference.val;\n } catch {\n return undefined;\n }\n }\n\n private isConditionValidForComparison(condition: Condition): boolean {\n return Array.isArray(condition.targetValues) && condition.targetValues.length > 0;\n }\n}\n","import { rapidhash } from \"rapidhash-js\";\n\nconst SEED = 0x397832987n;\n\nexport const assignPercentage = (data: {configId: string, contextIdentifier: string}): number => {\n const value = `${data.contextIdentifier}-${data.configId}`;\n\n return Number(rapidhash(value, { seed: SEED }) % 1_000n) / 10.0;\n};\n","import { ConditionEvaluator } from \"./ConditionEvaluator\";\nimport { assignPercentage } from \"./percent-hashing\";\nimport {\n ConditionalRule,\n Config,\n EvaluationContext,\n ConfigState,\n Percentage,\n PercentageRule,\n Rule,\n Condition,\n ConfigDirectorLogger,\n} from \"./types\";\n\ntype RuleSuccess = {\n success: true;\n value: string;\n};\n\ntype RuleFailure = {\n success: false;\n};\n\ntype RuleEvaluationResult = RuleSuccess | RuleFailure;\n\nexport class ConfigEvaluator {\n private readonly conditionEvaluator = new ConditionEvaluator();\n\n constructor(private readonly logger: ConfigDirectorLogger) {\n if (!logger || !logger.warn || !logger.error || !logger.info) {\n throw new TypeError(\"The provided logger is not a valid 'ConfigDirectorLogger'\");\n }\n\n this.logger = logger;\n }\n\n public evaluate(config: Config, context?: EvaluationContext): ConfigState {\n return {\n id: config.id,\n key: config.key,\n type: config.type,\n value: this.getConfigValue(config, context),\n };\n }\n\n private getConfigValue(config: Config, context?: EvaluationContext) {\n const rules = [...(config.target?.rules ?? [])].sort(\n (a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER),\n );\n for (const rule of rules) {\n const result = this.evaluateRule(rule, config, context);\n if (result.success) {\n return result.value;\n }\n }\n return config.target?.defaultValue;\n }\n\n private evaluateRule(rule: Rule, config: Config, context?: EvaluationContext): RuleEvaluationResult {\n try {\n if (rule.type == \"percentage\") {\n return this.evaluatePercentageRule(rule, config, context);\n } else if (rule.type == \"conditional\") {\n return this.evaluateConditionalRule(rule, config, context);\n }\n } catch (error) {\n this.logger.warn(`There was an error while evaluating a targeting rule '${rule?.id}' for '${config?.key}'. The rule will be disregarded.`, {\n error,\n configKey: config?.key,\n ruleId: rule?.id,\n });\n }\n\n return { success: false };\n }\n\n private evaluatePercentageRule(\n rule: PercentageRule,\n config: Config,\n context?: EvaluationContext,\n ): RuleEvaluationResult {\n return this.evaluatePercentage(rule.percentages ?? [], config, context);\n }\n\n private evaluatePercentage(\n percentages: Percentage[],\n config: Config,\n context?: EvaluationContext,\n ): RuleEvaluationResult {\n const assignedPercentage = assignPercentage({\n configId: config.id,\n contextIdentifier: context?.context?.id ?? crypto.randomUUID(),\n });\n let index = 0;\n let sum = 0.0;\n let bucket: Percentage | undefined = undefined;\n while (!bucket && index < percentages.length) {\n if (assignedPercentage <= percentages[index].percentage + sum) {\n bucket = percentages[index];\n }\n\n sum += percentages[index].percentage;\n index += 1;\n }\n\n if (bucket?.value != null) {\n return { success: true, value: bucket.value.toString() };\n }\n return { success: false };\n }\n\n private evaluateConditionalRule(\n rule: ConditionalRule,\n config: Config,\n context?: EvaluationContext,\n ): RuleEvaluationResult {\n const conditions = rule.conditions ?? [];\n let index = 0;\n let condition: Condition | undefined = undefined;\n while (!condition && index < conditions.length) {\n if (this.conditionEvaluator.evaluate(conditions[index], context)) {\n condition = conditions[index];\n }\n index += 1;\n }\n\n if (condition && rule.target == \"value\" && rule.value != null) {\n return { success: true, value: rule.value.toString() };\n } else if (condition && rule.target == \"percentage\") {\n return this.evaluatePercentage(rule.percentages ?? [], config, context);\n }\n return { success: false };\n }\n}\n","import type { ConfigState, ConfigValueType, EvaluationReason } from \"./types\";\n\ntype NativeType =\n | \"string\"\n | \"number\"\n | \"bigint\"\n | \"boolean\"\n | \"symbol\"\n | \"undefined\"\n | \"object\"\n | \"function\";\n\ntype ParseResult<T extends ConfigValueType> = {\n parsedValue: T;\n requestedType: NativeType | string;\n usedDefault: boolean;\n reason: EvaluationReason;\n};\n\nexport const getRequestedType = <T extends ConfigValueType>(defaultValue: T): string => {\n const baseType = typeof defaultValue;\n if (baseType === \"object\") {\n try {\n return (defaultValue as object).constructor?.name ?? baseType;\n } catch {\n return baseType;\n }\n } else if (baseType === \"function\") {\n try {\n const functionName = (defaultValue as any).name;\n return functionName ? `function: ${functionName}` : baseType;\n } catch {\n return baseType;\n }\n }\n return baseType;\n};\n\nconst isNumericNativeType = (requestedType: NativeType): boolean => {\n return requestedType === \"number\" || requestedType === \"bigint\";\n};\n\nexport const parseConfigValue = <T extends ConfigValueType>(\n configState: ConfigState,\n defaultValue: T,\n): ParseResult<T> => {\n const value = configState.value;\n const requestedType = getRequestedType(defaultValue);\n\n if (!value) {\n return {\n parsedValue: defaultValue as T,\n requestedType,\n usedDefault: true,\n reason: \"value-missing\",\n };\n }\n\n if (typeof defaultValue === \"string\") {\n return {\n parsedValue: value as T,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n }\n\n if (typeof defaultValue === \"boolean\" && configState.type === \"boolean\") {\n const boolValue = parseConfigBoolean(value);\n const hasBoolean = typeof boolValue === \"boolean\";\n return {\n parsedValue: (hasBoolean ? boolValue : defaultValue) as T,\n requestedType,\n usedDefault: !hasBoolean,\n reason: hasBoolean ? \"found-match\" : \"invalid-boolean\",\n };\n }\n\n if (isNumericNativeType(typeof defaultValue) && configState.type === \"integer\") {\n const numValue = parseConfigInteger(value);\n const hasNumber = typeof numValue === \"number\";\n return {\n parsedValue: (hasNumber ? numValue : defaultValue) as T,\n requestedType,\n usedDefault: !hasNumber,\n reason: hasNumber ? \"found-match\" : \"invalid-number\",\n };\n }\n if (isNumericNativeType(typeof defaultValue) && (configState.type === \"float\" || configState.type === \"enum\" )) {\n const numValue = parseConfigFloat(value);\n const hasNumber = typeof numValue === \"number\";\n return {\n parsedValue: (hasNumber ? numValue : defaultValue) as T,\n requestedType,\n usedDefault: !hasNumber,\n reason: hasNumber ? \"found-match\" : \"invalid-number\",\n };\n }\n\n return {\n parsedValue: value as T,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n};\n\nconst parseConfigBoolean = (value: string): boolean | undefined => {\n if (!value) {\n return;\n }\n const lowerValue = value.toLowerCase();\n if (lowerValue != \"true\" && lowerValue != \"false\") {\n return;\n }\n return lowerValue === \"true\";\n};\n\nconst parseConfigInteger = (value: string): number | undefined => {\n if (!value) {\n return;\n }\n const num = Number.parseInt(value);\n if (isNaN(num) || !isFinite(num)) {\n return;\n }\n return num;\n};\n\nconst parseConfigFloat = (value: string): number | undefined => {\n if (!value) {\n return;\n }\n const num = Number.parseFloat(value);\n if (isNaN(num) || !isFinite(num)) {\n return;\n }\n return num;\n};\n","import { ConfigDirectorLogger, ConfigDirectorLogMessageDecorator, ConfigDirectorLoggingLevel } from \"./types\";\n\nconst LEVELS: Record<ConfigDirectorLoggingLevel, number> = {\n off: -1,\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\nconst buildDateFormatter = () => {\n const baseOptions: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZoneName: \"short\",\n };\n\n try {\n return new Intl.DateTimeFormat(\n undefined,\n { ...baseOptions, fractionalSecondDigits: 3 } as Intl.DateTimeFormatOptions,\n );\n } catch {\n return new Intl.DateTimeFormat(undefined, baseOptions);\n }\n};\n\nexport class DefaultConsoleLogger implements ConfigDirectorLogger {\n private readonly dateFormatter;\n\n constructor(private readonly level: ConfigDirectorLoggingLevel, private readonly decorator: ConfigDirectorLogMessageDecorator) {\n this.level = level;\n this.decorator = decorator;\n this.dateFormatter = buildDateFormatter();\n }\n\n debug(message: string, ...args: any): void {\n this.log(console.debug, \"debug\", message, ...args);\n }\n\n info(message: string, ...args: any): void {\n this.log(console.info, \"info\", message, ...args);\n }\n\n warn(message: string, ...args: any): void {\n this.log(console.warn, \"warn\", message, ...args);\n }\n\n error(message: string, ...args: any): void {\n this.log(console.error, \"error\", message, ...args);\n }\n\n private log(loggerFunction: (...args: any) => void, level: ConfigDirectorLoggingLevel, message: string, ...args: any) {\n if (LEVELS[this.level] >= LEVELS[level]) {\n loggerFunction(`[${this.dateFormatter.format(new Date())}] ${this.decorator?.decorateMessage(message)}`, ...args);\n }\n }\n}\n","import { DefaultConsoleLogger } from \"@shared/logger\";\nimport { ConfigDirectorLoggingLevel, ConfigDirectorLogMessageDecorator } from \"@shared/types\";\n\nclass LogMessageDecorator implements ConfigDirectorLogMessageDecorator {\n decorateMessage(message: string): string {\n return `[ConfigDirector:js-server-sdk] ${message}`;\n }\n}\n\nexport const createDefaultLogger = (\n level?: ConfigDirectorLoggingLevel,\n messageDecorator?: ConfigDirectorLogMessageDecorator,\n) => {\n return new DefaultConsoleLogger(level ?? \"warn\", messageDecorator ?? new LogMessageDecorator());\n};\n","import { DroppedEvents } from \"./types\";\n\nexport const djb2Hash = (data: string): string => {\n const hash = djb2(new TextEncoder().encode(data));\n return hash.toString(16).padStart(8, \"0\");\n};\n\nconst djb2 = (bytes: Uint8Array): number => {\n let hash = 5381;\n for (let i = 0; i < bytes.length; i++) {\n hash = (hash << 5) + hash + bytes[i];\n hash = hash >>> 0;\n }\n return hash;\n};\n\nexport const isEventListEmpty = <T extends Record<string | symbol, any[]>>(eventList: T): boolean => {\n const keys = Object.keys(eventList);\n if (keys.length == 0) {\n return true;\n }\n\n for (const key of keys) {\n if ((eventList[key]?.length ?? 0) > 0) {\n return false;\n }\n }\n\n return true;\n};\n\nexport const isDroppedEventsEmpty = (droppedEvents?: DroppedEvents): boolean => {\n if (!droppedEvents) {\n return true;\n }\n const keys = Object.keys(droppedEvents);\n if (keys.length == 0) {\n return true;\n }\n\n for (const key of keys) {\n if ((droppedEvents[key] ?? 0) > 0) {\n return false;\n }\n }\n\n return true;\n};\n","import { ConfigDirectorLogger } from \"./types\";\n\nexport const fetchWithTimeout = async (\n timeout: number,\n resource: string | URL | Request,\n options: RequestInit | undefined,\n logger: ConfigDirectorLogger,\n) => {\n const abortController = new AbortController();\n const abortTimeoutId = setTimeout(() => {\n logger.debug(\"[fetchWithTimeout] Reached timeout, aborting request\");\n abortController.abort();\n }, timeout);\n\n try {\n const response = await fetch(resource, {\n ...options,\n signal: abortController.signal,\n });\n clearTimeout(abortTimeoutId);\n return response;\n } catch (error) {\n logger.warn(\"[fetchWithTimeout] Fetch error: \", error);\n clearTimeout(abortTimeoutId);\n throw error;\n }\n};\n","import { isDroppedEventsEmpty, isEventListEmpty } from \"@shared/telemetry/utils\";\nimport { ConfigDirectorLogger } from \"../types\";\nimport { EventReport, EventReporter, EventReporterPayload, ReporterResponse } from \"./types\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\n\nexport type EventReporterOptions = {\n sdkKey: string;\n logger: ConfigDirectorLogger;\n baseUrl: URL;\n};\n\nexport class ServerEventReporter implements EventReporter {\n private readonly sdkKey: string;\n private readonly logger: ConfigDirectorLogger;\n private readonly url: URL;\n private executeRequests = true;\n\n constructor(options: EventReporterOptions) {\n this.sdkKey = options.sdkKey;\n this.logger = options.logger;\n this.url = new URL(\"server/telemetry/v1\", options.baseUrl);\n }\n\n public async report({\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n }: EventReporterPayload): Promise<ReporterResponse> {\n if (!this.executeRequests) {\n return { success: false, fatalError: true };\n }\n\n const eventReport: EventReport = {\n serverSdkKey: this.sdkKey,\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n };\n if (this.isReportEmpty(eventReport)) {\n return { success: true, fatalError: false };\n }\n\n const response = await this.sendReport(eventReport);\n if (response.fatalError) {\n this.executeRequests = false;\n }\n return response;\n }\n\n private isReportEmpty(eventReport: EventReport) {\n return (\n isEventListEmpty(eventReport.discreteEvents) &&\n isEventListEmpty(eventReport.aggregatedEvents) &&\n isDroppedEventsEmpty(eventReport.droppedEvents)\n );\n }\n\n private async sendReport(eventReport: EventReport): Promise<ReporterResponse> {\n try {\n const response = await fetchWithTimeout(\n 5_000,\n this.url,\n {\n method: \"POST\",\n body: JSON.stringify(eventReport),\n },\n this.logger,\n );\n\n const isFatalError = this.isStatusFatal(response.status);\n if (isFatalError) {\n this.logger.warn(\n `[EventReporter] Received a fatal status response (${response.status}) from the telemetry endpoint. No more telemetry data will be sent.`,\n );\n }\n return { success: response.ok, fatalError: isFatalError };\n } catch (error) {\n this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${error}`, { error });\n return { success: false, fatalError: false };\n }\n }\n\n private isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500;\n }\n}\n","import { AggregatedEvent, EventQueueSnapshot, ReportableEvent } from \"./types\";\n\nexport class EventAggregator {\n public aggregate<T extends ReportableEvent>(snapshot: EventQueueSnapshot<T>): AggregatedEvent<T>[] {\n if (snapshot.events.length == 0) {\n return [];\n }\n\n const map = new Map<string, { event: T; count: number }>();\n for (const event of snapshot.events) {\n const serializedEvent = JSON.stringify(event);\n const count = map.get(serializedEvent)?.count || 0;\n map.set(serializedEvent, { event, count: count + 1 });\n }\n return Array.from(map).map(([, v]) => {\n return {\n startTime: snapshot.startTime,\n endTime: snapshot.endTime,\n count: v.count,\n event: v.event,\n };\n });\n }\n}\n","import { EventQueueSnapshot, ReportableEvent } from \"./types\";\n\nexport class EventQueue<T extends ReportableEvent> {\n private readonly limit: number;\n private startTime?: Date;\n private _events: T[] = [];\n private _droppedEventCount = 0;\n\n constructor(limit?: number) {\n this.limit = limit ?? 1_000;\n }\n\n public get events(): T[] {\n return this._events;\n }\n\n public get reachedLimit(): boolean {\n return this._events.length >= this.limit;\n }\n\n public get droppedEventCount(): number {\n return this._droppedEventCount;\n }\n\n public push(...newEvents: T[]) {\n if (!this.startTime) {\n this.startTime = new Date();\n }\n\n const newEventsCountToDrop = Math.max(0, newEvents.length - this.limit);\n const eventsToPush = newEvents.slice(newEventsCountToDrop, newEvents.length);\n this._droppedEventCount += newEventsCountToDrop;\n if (this._events.length + eventsToPush.length > this.limit) {\n const eventCountToDrop = this._events.length + eventsToPush.length - this.limit;\n this._events.splice(0, eventCountToDrop);\n this._droppedEventCount += eventCountToDrop;\n }\n return this._events.push(...eventsToPush);\n }\n\n public takeSnapshot(): EventQueueSnapshot<T> {\n const endTime = new Date();\n const startTime = this.startTime ?? endTime;\n const eventsSnapshot = this._events.splice(0);\n const droppedCount = this._droppedEventCount;\n this.startTime = undefined;\n this._droppedEventCount = 0;\n return {\n startTime,\n endTime,\n events: eventsSnapshot,\n droppedCount,\n };\n }\n\n public clear() {\n this._events = [];\n this.startTime = undefined;\n this._droppedEventCount = 0;\n }\n}\n","import { ConfigDirectorContext, ConfigDirectorLogger, ConfigType, ConfigValueType } from \"../types\";\nimport { EventAggregator } from \"./EventAggregator\";\nimport { EventQueue } from \"./EventQueue\";\nimport { djb2Hash } from \"./utils\";\nimport { EventReporter, ReportableEvent, ReporterResponse } from \"./types\";\n\nconst CONFIG_VALUE_MAX_LENGTH = 500;\n\nexport type TelemetryEventCollectorOptions = {\n sdkKey: string;\n logger: ConfigDirectorLogger;\n baseUrl: URL;\n flushIntervalDelay?: number;\n initialFlushIntervalDelay?: number;\n evaluationQueueLimit?: number;\n};\n\nexport abstract class TelemetryEventCollector<T extends ReportableEvent> {\n protected readonly logger: ConfigDirectorLogger;\n protected abstract readonly reporter: EventReporter | undefined;\n protected readonly evaluationEventQueue: EventQueue<T>;\n protected readonly aggregator: EventAggregator = new EventAggregator();\n protected flushIntervalDelay: number;\n protected flushTimeout: ReturnType<typeof setTimeout>;\n protected collectEvents = true;\n protected abstract _context?: ConfigDirectorContext;\n\n constructor(options: TelemetryEventCollectorOptions) {\n this.logger = options.logger;\n this.evaluationEventQueue = new EventQueue(options.evaluationQueueLimit ?? 1_000);\n this.flushIntervalDelay = options.flushIntervalDelay ?? 30_000;\n const initialDelay = options.initialFlushIntervalDelay ?? 5_000;\n this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), initialDelay);\n }\n\n protected sanitizeValue<TV extends ConfigValueType>(value: TV, type?: ConfigType): string {\n if (type === \"json\") {\n try {\n const json = JSON.stringify(value);\n return djb2Hash(json);\n } catch {\n return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);\n }\n }\n\n return value.toString().slice(0, CONFIG_VALUE_MAX_LENGTH);\n }\n\n protected async flushAndScheduleNext() {\n const response = await this.flush();\n if (response.fatalError) {\n this.collectEvents = false;\n this.close();\n this.logger.warn(\n \"[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.\",\n );\n } else {\n this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);\n }\n }\n\n protected async flush(): Promise<ReporterResponse> {\n if (!this.reporter) {\n return { success: false, fatalError: false };\n }\n const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();\n const response = await this.reporter?.report({\n context: this._context,\n discreteEvents: {},\n aggregatedEvents: {\n evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),\n },\n droppedEvents: {\n evaluatedConfig: evaluationSnapshot.droppedCount,\n },\n });\n return response;\n }\n\n protected cancelScheduledFlush() {\n clearTimeout(this.flushTimeout);\n }\n\n public close() {\n this.collectEvents = false;\n clearTimeout(this.flushTimeout);\n this.flush();\n this.evaluationEventQueue.clear();\n }\n\n public dispose() {\n this.close();\n }\n}\n","export class LimitedMap<K, V> extends Map<K, V> {\n private readonly limit: number;\n private _droppedCount: number = 0;\n\n constructor(limit: number, entries?: [K, V][]) {\n super(entries);\n this.limit = limit;\n this.enforceLimit();\n }\n\n public override set(key: K, value: V): this {\n super.set(key, value);\n this.enforceLimit();\n return this;\n }\n\n public get droppedCount(): number {\n return this._droppedCount;\n }\n\n public clearAndReset() {\n this.clear();\n this._droppedCount = 0;\n }\n\n private enforceLimit() {\n while (this.size > this.limit) {\n const oldestKey = this.keys().next().value;\n if (oldestKey !== undefined) {\n this.delete(oldestKey);\n this._droppedCount += 1;\n }\n }\n }\n}\n","import { ConfigDirectorContext, ConfigValueType } from \"../types\";\nimport { ServerEventReporter } from \"./ServerEventReporter\";\nimport { EvaluatedConfigEvent } from \"./telemetry-events\";\nimport { EvaluatedConfig, EventReporter, ReporterResponse, ServerTelemetryEventCollectorOptions } from \"./types\";\nimport { TelemetryEventCollector } from \"@shared/telemetry/TelemetryEventCollector\";\nimport { LimitedMap } from \"@shared/LimitedMap\";\n\nconst DEFAULT_CONTEXTS_LIMIT = 1_000;\n\nexport class ServerTelemetryEventCollector extends TelemetryEventCollector<EvaluatedConfigEvent<string>> {\n protected readonly reporter: EventReporter;\n protected _context?: ConfigDirectorContext;\n private readonly contexts: LimitedMap<string, ConfigDirectorContext>;\n\n constructor(options: ServerTelemetryEventCollectorOptions) {\n super(options);\n this.reporter = new ServerEventReporter(options);\n this.contexts = new LimitedMap(options.contextLimit ?? DEFAULT_CONTEXTS_LIMIT);\n }\n\n public evaluatedConfig<T extends ConfigValueType>(payload: EvaluatedConfig<T>): void {\n if (!this.collectEvents) {\n return;\n }\n if (payload.context?.id) {\n this.contexts.set(payload.context.id, payload.context);\n }\n const event = {\n ...payload.evaluation,\n contextId: payload.context?.id,\n };\n\n this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(event));\n }\n\n private sanitizeEvaluatedConfigEvent<T extends ConfigValueType>(\n event: EvaluatedConfigEvent<T>,\n ): EvaluatedConfigEvent<string> {\n return {\n ...event,\n defaultValue: this.sanitizeValue(event.defaultValue, event.type),\n evaluatedValue: this.sanitizeValue(event.evaluatedValue, event.type),\n };\n }\n\n private snapshotContexts() {\n const contexts = Array.from(this.contexts.values());\n const droppedCount = this.contexts.droppedCount;\n this.contexts.clearAndReset();\n return { contexts, droppedCount };\n }\n\n protected override async flush(): Promise<ReporterResponse> {\n if (!this.reporter) {\n return { success: false, fatalError: false };\n }\n const evaluationSnapshot = this.evaluationEventQueue.takeSnapshot();\n const contextsSnapshot = this.snapshotContexts();\n const response = await this.reporter?.report({\n context: this._context,\n discreteEvents: {\n capturedContexts: contextsSnapshot.contexts,\n },\n aggregatedEvents: {\n evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),\n },\n droppedEvents: {\n evaluatedConfig: evaluationSnapshot.droppedCount,\n capturedContexts: contextsSnapshot.droppedCount,\n },\n });\n return response;\n }\n}\n","import { StreamingTransport } from \"./StreamingTransport\";\nimport { ConfigEvaluator } from \"@config-evaluator/ConfigEvaluator\";\nimport { getRequestedType, parseConfigValue } from \"@shared/value-parser\";\nimport {\n ConfigDirectorContext,\n ConfigDirectorClientOptions,\n ConfigDirectorClient,\n ClientEvents,\n WatchHandler,\n ConfigValueType,\n ConfigDirectorLogger,\n Transport,\n IdentifyingSdkOptions,\n ConfigBundle,\n ConfigDefinition,\n} from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\nimport { ConfigDirectorValidationError } from \"@shared/errors\";\nimport EventEmitter from \"node:events\";\nimport { ConfigDirectorMetaContext } from \"@shared/types\";\nimport { ServerTelemetryEventCollector } from \"./telemetry\";\n\nconst defaultBaseUrl = new URL(\"https://server-sdk-api.configdirector.com\");\nconst DEFAULT_FLUSH_INTERVAL = 30_000;\nconst DEFAULT_EVENT_QUEUE_LIMIT = 5_000;\n\ntype WatchHandlerWithOptions<T extends ConfigValueType> = {\n handler: WatchHandler<T>;\n defaultValue: T;\n requestedType: string;\n context?: ConfigDirectorContext;\n};\n\nexport class DefaultConfigDirectorClient implements ConfigDirectorClient {\n private logger: ConfigDirectorLogger;\n private usageEventCollector: ServerTelemetryEventCollector;\n private configSet: ConfigBundle | undefined;\n private handlersMap: Map<string, WatchHandlerWithOptions<any>[]> = new Map();\n private transport: Transport;\n private eventEmitter = new EventEmitter();\n private timeout: number;\n private ready = false;\n private readyPromise: Promise<void> | undefined;\n private readyResolve: (() => void) | undefined;\n private streaming: boolean;\n private configEvaluator: ConfigEvaluator;\n private metaContext: ConfigDirectorMetaContext;\n\n constructor(\n serverSdkKey: string,\n sdkOptions: IdentifyingSdkOptions,\n clientOptions?: ConfigDirectorClientOptions,\n ) {\n this.logger = clientOptions?.logger ?? createDefaultLogger();\n this.timeout = clientOptions?.connection?.timeout ?? 3_000;\n this.metaContext = clientOptions?.metadata ?? {};\n this.configEvaluator = new ConfigEvaluator(this.logger);\n const baseUrl = this.parseUrl(clientOptions?.connection?.url) ?? defaultBaseUrl;\n this.streaming = clientOptions?.connection?.streaming === false ? false : true;\n const transportConstructor = StreamingTransport;\n const queueLimit = clientOptions?.telemetry?.eventQueueLimit ?? DEFAULT_EVENT_QUEUE_LIMIT;\n this.usageEventCollector = new ServerTelemetryEventCollector({\n sdkKey: serverSdkKey,\n logger: this.logger,\n baseUrl,\n flushIntervalDelay: clientOptions?.telemetry?.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n evaluationQueueLimit: Math.ceil(queueLimit * 0.7),\n contextLimit: Math.floor(queueLimit * 0.3),\n });\n this.transport = new transportConstructor({\n serverSdkKey,\n baseUrl,\n metaContext: {\n ...clientOptions?.metadata,\n sdkName: sdkOptions.sdkName,\n sdkVersion: sdkOptions.sdkVersion,\n },\n logger: this.logger,\n });\n\n this.transport.on(\"configBundleReceived\", (configBundle: ConfigBundle) => {\n this.readyResolve?.();\n const configKeys = Object.keys(configBundle.configs);\n if (!this.configSet || configBundle.kind == \"full\") {\n this.configSet = configBundle;\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configBundle.configs);\n } else {\n this.configSet.configs = {\n ...this.configSet.configs,\n ...configBundle.configs,\n };\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configBundle.configs);\n }\n this.logger.debug(\n `[ConfigDirectorClient] ConfigSet updated from server with ${configKeys.length} key(s)`,\n { keys: configKeys },\n );\n });\n }\n\n public async initialize() {\n try {\n this.ready = false;\n this.readyPromise = new Promise<void>((resolve) => {\n this.readyResolve = resolve;\n }).then(() => {\n this.ready = true;\n this.eventEmitter.emit(\"clientReady\");\n this.logger.debug(\"[ConfigDirectorClient] Received initial payload from the server, client is ready\");\n });\n const startTime = new Date().getTime();\n await this.transport.connect(this.timeout);\n const elapsedTime = new Date().getTime() - startTime;\n const remainingTimeout = this.timeout - elapsedTime;\n if (remainingTimeout > 0) {\n await Promise.race([\n this.readyPromise,\n new Promise<void>((resolve) => {\n setTimeout(() => resolve(), remainingTimeout);\n }),\n ]);\n }\n if (!this.ready) {\n const warningDetails = this.streaming\n ? \"The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.\"\n : \"Since the client was configured without streaming, configs may not update and always return the default value.\";\n this.logger.warn(\n `[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${warningDetails}`,\n );\n }\n } catch (error) {\n this.logger.error(\"[ConfigDirectorClient] An error occurred during initialization: \", error);\n }\n }\n\n private updateWatchers(configsMap: Record<string, ConfigDefinition>) {\n Object.values(configsMap).forEach((v) => this.updateWatchersForConfig(v as ConfigDefinition));\n }\n\n private updateWatchersForConfig(configDefinition: ConfigDefinition) {\n this.handlersMap.get(configDefinition.key)?.forEach((h) => {\n const value = this.getValueFromConfigDefinition(\n configDefinition.key,\n configDefinition,\n h.defaultValue,\n h.context,\n );\n h.handler(value);\n });\n }\n\n public watch<T extends ConfigValueType>(\n configKey: string,\n defaultValue: T,\n callback: WatchHandler<T>,\n context?: ConfigDirectorContext,\n ) {\n this.validateDefaultValue(defaultValue);\n\n const handlers = this.handlersMap.get(configKey);\n const handlerWithOptions = {\n handler: callback,\n defaultValue,\n requestedType: typeof defaultValue,\n context,\n };\n if (handlers) {\n handlers.push(handlerWithOptions);\n } else {\n this.handlersMap.set(configKey, [handlerWithOptions]);\n }\n\n return () => this.unwatch(configKey, callback);\n }\n\n public unwatch<T extends ConfigValueType>(configKey: string, callback?: WatchHandler<T>) {\n const handlers = this.handlersMap.get(configKey);\n if (!handlers) {\n return;\n }\n\n if (callback) {\n const index = handlers.findIndex((h) => h.handler == callback);\n if (index >= 0) {\n handlers?.splice(index, 1);\n }\n } else {\n handlers.splice(0);\n }\n }\n\n public getValue<T extends ConfigValueType>(\n configKey: string,\n defaultValue: T,\n context?: ConfigDirectorContext,\n ): T {\n this.validateDefaultValue(defaultValue);\n\n const configDefinition = this.configSet?.configs[configKey];\n return this.getValueFromConfigDefinition(configKey, configDefinition, defaultValue, context);\n }\n\n private getValueFromConfigDefinition<T extends ConfigValueType>(\n configKey: string,\n configDefinition: ConfigDefinition | undefined,\n defaultValue: T,\n context?: ConfigDirectorContext,\n ): T {\n if (configDefinition) {\n const configState = this.configEvaluator.evaluate(configDefinition, {\n context,\n metadata: this.metaContext,\n });\n const parseResult = parseConfigValue<T>(configState, defaultValue);\n this.usageEventCollector.evaluatedConfig({\n context,\n evaluation: {\n key: configKey,\n defaultValue: defaultValue,\n requestedType: parseResult.requestedType,\n evaluatedValue: parseResult.parsedValue,\n usedDefault: parseResult.usedDefault,\n evaluationReason: parseResult.reason,\n },\n });\n this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);\n return parseResult.parsedValue;\n } else {\n this.logger.debug(\n `[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`,\n );\n this.usageEventCollector.evaluatedConfig({\n context,\n evaluation: {\n key: configKey,\n defaultValue: defaultValue,\n requestedType: getRequestedType(defaultValue),\n evaluatedValue: defaultValue,\n usedDefault: true,\n evaluationReason: \"config-state-missing\",\n },\n });\n return defaultValue;\n }\n }\n\n private parseUrl(url: string | undefined): URL | undefined {\n if (!url) {\n return;\n }\n\n try {\n return new URL(url);\n } catch (error) {\n throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);\n }\n }\n\n private validateDefaultValue<T extends ConfigValueType>(defaultValue: T) {\n if (defaultValue === undefined || defaultValue === null) {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config must be defined and non-null.\",\n );\n }\n\n if (typeof defaultValue === \"function\") {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config cannot be a function.\",\n );\n }\n }\n\n public get isReady(): boolean {\n return this.ready;\n }\n\n public on<T extends keyof ClientEvents>(eventName: T, handler: (args: ClientEvents[T]) => void): void {\n this.eventEmitter.on(eventName, handler);\n }\n\n public off<T extends keyof ClientEvents>(eventName: T, handler?: (args: ClientEvents[T]) => void) {\n if (handler) {\n this.eventEmitter.off(eventName, handler);\n } else {\n this.eventEmitter.removeAllListeners(eventName);\n }\n }\n\n public removeAllObservers() {\n this.logger.debug(\"[ConfigDirectorClient] removeAllObservers() has been called, removing all observers\");\n this.eventEmitter.removeAllListeners();\n this.handlersMap.clear();\n }\n\n public unwatchAll() {\n this.handlersMap.clear();\n }\n\n public closeConnection() {\n this.logger.debug(\n \"[ConfigDirectorClient] closeConnection() has been called, closing connection to server\",\n );\n this.transport.close();\n this.ready = false;\n }\n\n public dispose() {\n this.removeAllObservers();\n this.closeConnection();\n }\n}\n","import { ConfigDirectorLoggingLevel, ConfigDirectorLogMessageDecorator } from \"@shared/types\";\nimport { DefaultConfigDirectorClient } from \"./DefaultConfigDirectorClient\";\nimport { ConfigDirectorClient, ConfigDirectorClientOptions } from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\n\nexport const createClient = (\n serverSdkKey: string,\n clientOptions?: ConfigDirectorClientOptions,\n): ConfigDirectorClient => {\n return new DefaultConfigDirectorClient(\n serverSdkKey,\n { sdkName: \"js-server-sdk\", sdkVersion: \"__VERSION__\" },\n clientOptions,\n );\n};\n\nexport const createConsoleLogger = (\n level: ConfigDirectorLoggingLevel,\n messageDecorator?: ConfigDirectorLogMessageDecorator,\n) => {\n return createDefaultLogger(level, messageDecorator);\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED