@configdirector/client-sdk 0.1.10 → 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-client.cjs +3 -1
- package/dist/configdirector-client.d.cts +4 -0
- package/dist/configdirector-client.d.cts.map +1 -1
- package/dist/configdirector-client.d.mts +4 -0
- package/dist/configdirector-client.d.mts.map +1 -1
- package/dist/configdirector-client.mjs +3 -1
- package/dist/configdirector-client.mjs.map +1 -1
- package/package.json +2 -6
|
@@ -1 +1,3 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`eventsource-client`);var t=class{constructor(){this.handlerMap=new Map}on(e,t){let n=this.handlerMap.get(e);n?n.push(t):this.handlerMap.set(e,[t])}once(e,t){let n=this;function r(i){n.off(e,r),t.apply(n,i)}this.on(e,r)}off(e,t){let n=this.handlerMap.get(e);if(n)if(t){let e=n.indexOf(t);e>=0&&n.splice(e,1)}else this.handlerMap.set(e,[])}clear(){this.handlerMap.clear()}emit(e,t){let n=this.handlerMap.get(e);n&&n.slice().map(e=>e(t))}},n=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},r=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}};const i=e=>{if(e instanceof DOMException){if(e.name===`NotAllowedError`)return!0}else if(e instanceof TypeError)return!0;return!1};var a=class{constructor(e){this.options=e,this.eventEmitter=new t,this.options=e,this.logger=e.logger,this.url=new URL(`client/sse/v1`,e.baseUrl)}async connect(t,n){this.eventSource&&this.close();let r,i,a=async(e,t)=>{let n=await fetch(e,t);return r=n.status,n.ok||(i=await n.text()),n},o=new Promise((n,o)=>{this.eventSource=(0,e.createEventSource)({url:this.url,fetch:a,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:t,metaContext:this.options.metaContext,clientSdkKey:this.options.clientSdkKey}),onMessage:({data:e})=>{this.dispatchMessage(e)},onConnect:()=>{r&&this.isStatusFatal(r)?(this.close(),o(this.prepareFatalError(r,i))):(this.logger.debug(`[EventSourceTransport] Connected, status: %s`,r),n(this))},onScheduleReconnect:e=>{r&&this.isStatusFatal(r)?(this.close(),o(this.prepareFatalError(r,i))):this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${e.delay}. Response status: ${r}`)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}})});return Promise.race([o,new Promise(e=>{setTimeout(()=>e(this),n)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configSetReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,t){let r=e??0;return new n(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, will not attempt to reconnect.`,r)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){this.eventSource?.close()}dispose(){this.close(),this.clear()}};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},s=e=>e===`number`||e===`bigint`,c=(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=l(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=u(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=d(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`}},l=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},u=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},d=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},f={off:-1,error:0,warn:1,info:2,debug:3},p=()=>{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 m=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=p()}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){f[this.level]>=f[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},h=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const g=(e,t)=>new m(e??`warn`,t??new h),_=e=>v(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),v=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},y=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},b=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};var x=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let i={clientSdkKey:this.sdkKey,context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(i))return{success:!0,fatalError:!1};let a=this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return y(e.discreteEvents)&&y(e.aggregatedEvents)&&b(e.droppedEvents)}sendReport(e){try{return{success:navigator.sendBeacon(this.url,JSON.stringify(e)),fatalError:!1}}catch(e){return this.logger.warn(`[EventReporter] Fatal error attempting to send telemetry data: ${e}. No more telemetry data will be sent.`),{success:!1,fatalError:!0}}}},S=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}))}},C=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}},w=class{constructor(e){this.aggregator=new S,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new C(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 _(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()}},T=class extends w{constructor(e){super(e),this.reporter=new x(e);try{document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.flush()})}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(e))}sanitizeEvaluatedConfigEvent(e){return{...e,defaultValue:this.sanitizeValue(e.defaultValue,e.type),evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type)}}};const E=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 D=class{constructor(e){this.options=e,this.eventEmitter=new t,this.fatalError=!1,this.options=e,this.logger=e.logger,this.url=new URL(`client/pull/v1`,e.baseUrl)}async connect(e,t){if(this.fatalError)return this.logger.warn(`[PullTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`),this;try{let r=await E(t,this.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:this.options.metaContext,clientSdkKey:this.options.clientSdkKey})},this.logger);if(!r.ok)throw this.isStatusFatal(r.status)?(this.fatalError=!0,this.prepareFatalResponseStatusError(r.status,await r.text())):new n(`Connection failed with status: ${r.status}`,r.status);let i=JSON.parse(await r.text());return this.eventEmitter.emit(`configSetReceived`,i),this}catch(e){throw i(e)?(this.fatalError=!0,new n(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new n(`Failed to parse the response from the server: ${e}`):new n(`Connection failed with error: ${e}.`)}}prepareFatalResponseStatusError(e,t){let r=e??0;return new n(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,r)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){}dispose(){this.close(),this.clear()}};const O=new URL(`https://client-sdk-api.configdirector.com`);var k=class{constructor(e,n,r){this.handlersMap=new Map,this.eventEmitter=new t,this.ready=!1,this.logger=r?.logger??g(),this.timeout=r?.connection?.timeout??3e3;let i=this.parseUrl(r?.connection?.url)??O;this.streaming=r?.connection?.streaming!==!1;let o=this.streaming?a:D;this.usageEventCollector=new T({sdkKey:e,logger:this.logger,baseUrl:i}),this.transport=new o({clientSdkKey:e,baseUrl:i,metaContext:{...r?.metadata,sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:navigator?.userAgent},logger:this.logger}),this.transport.on(`configSetReceived`,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(e){await this.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}async connectToTransport(e,t){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.eventEmitter.emit(`clientReady`,{action:t}),this.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let n=new Date().getTime();await this.transport.connect(e??{},this.timeout),this.currentContext=e,this.usageEventCollector.updateContext(e);let r=new Date().getTime()-n,i=this.timeout-r;if(i>0&&await Promise.race([this.readyPromise,new Promise(e=>{setTimeout(()=>e(),i)})]),!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 ${t} after ${this.timeout}ms. ${e}`)}}catch(e){this.logger.error(`[ConfigDirectorClient] An error occurred during ${t}: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){this.handlersMap.get(e.key)?.forEach(t=>{let n=this.getValueFromConfigState(e.key,e,t.defaultValue);t.handler(n)})}watch(e,t,n){this.validateDefaultValue(t);let r=this.handlersMap.get(e),i={handler:n,defaultValue:t,requestedType:typeof t};return r?r.push(i):this.handlersMap.set(e,[i]),()=>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){this.validateDefaultValue(t);let n=this.configSet?.configs[e];return this.getValueFromConfigState(e,n,t)}getValueFromConfigState(e,t,n){if(!t)return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({contextId:this.currentContext?.id,key:e,defaultValue:n,requestedType:o(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n;let r=c(t,n);return this.usageEventCollector.evaluatedConfig({contextId:this.currentContext?.id,key:e,defaultValue:n,requestedType:r.requestedType,evaluatedValue:r.parsedValue,usedDefault:r.usedDefault,evaluationReason:r.reason}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${r.parsedValue}'`),r.parsedValue}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new r(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new r(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new r(`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){this.eventEmitter.off(e,t)}clear(){this.logger.debug(`[ConfigDirectorClient] clear() has been called, removing all observers`),this.eventEmitter.clear(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}close(){this.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.clear(),this.close()}};const A=(e,t)=>new k(e,{sdkName:`js-client-sdk`,sdkVersion:`0.1.10`},t),j=(e,t)=>g(e,t);exports.createClient=A,exports.createConsoleLogger=j;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class{constructor(){this.handlerMap=new Map}on(e,t){let n=this.handlerMap.get(e);n?n.push(t):this.handlerMap.set(e,[t])}once(e,t){let n=this;function r(i){n.off(e,r),t.apply(n,i)}this.on(e,r)}off(e,t){let n=this.handlerMap.get(e);if(n)if(t){let e=n.indexOf(t);e>=0&&n.splice(e,1)}else this.handlerMap.set(e,[])}clear(){this.handlerMap.clear()}emit(e,t){let n=this.handlerMap.get(e);n&&n.slice().map(e=>e(t))}},t=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},n=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}};const r=e=>{if(e instanceof DOMException){if(e.name===`NotAllowedError`)return!0}else if(e instanceof TypeError)return!0;return!1};var i=class e extends Error{constructor(t){super(t),this.name=`StreamClosedError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`MissingResponseBodyError`,Object.setPrototypeOf(this,e.prototype)}},o=class e extends Error{constructor(t){super(t),this.name=`InvalidOptionError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`ValueOutOfRangeError`,Object.setPrototypeOf(this,e.prototype)}},c=class{constructor(e){this.isFirstChunk=!0,this.previousIncompleteLine=``,this.currentEvent={data:``},this.onEvent=e.onEvent??(()=>{}),this.onRetry=e.onRetry,this.onComment=e.onComment}parse(e){let t=this.prepareChunk(e),n=this.splitLines(t);for(let e of n)this.parseAndProcessLine(e)}finish(){this.previousIncompleteLine!==``&&this.parseAndProcessLine(this.previousIncompleteLine)}parseAndProcessLine(e){if(e.startsWith(`:`)){this.onComment?.(this.getValueFromLine(0,e));return}if(e===``){this.publishEvent();return}let t=this.parseContentLine(e);this.processContent(t)}publishEvent(){let e=this.currentEvent.data.endsWith(`
|
|
2
|
+
`)?this.currentEvent.data.slice(0,-1):this.currentEvent.data;e&&this.onEvent({...this.currentEvent,id:this.lastEventId,data:e}),this.currentEvent={data:``}}processContent(e){switch(e.field){case`event`:this.currentEvent.type=e.value;break;case`data`:this.currentEvent.data+=e.value+`
|
|
3
|
+
`;break;case`id`:e.value.includes(`\0`)||(this.lastEventId=e.value);break;case`retry`:/^\d+$/.test(e.value)&&this.onRetry?.(Number.parseInt(e.value,10));break;default:break}}parseContentLine(e){let t=e.indexOf(`:`);return t===-1?{field:e,value:``}:{field:e.slice(0,t),value:this.getValueFromLine(t,e)}}splitLines(e){let t=e.split(/\r\n|\r|\n/);return this.previousIncompleteLine=t.pop()??``,t}getValueFromLine(e,t){if(e===-1)return``;let n=t[e+1]===` `?e+2:e+1;return t.slice(n)}prepareChunk(e){return this.isFirstChunk?(this.isFirstChunk=!1,this.previousIncompleteLine+e.replace(/^\xEF\xBB\xBF/,``).replace(/^\uFEFF/,``)):this.previousIncompleteLine+e}};let l=function(e){return e.OPEN=`open`,e.CLOSED=`closed`,e.CONNECTING=`connecting`,e}({});var u=class{constructor(e){this.reconnectAttempt=0,this.abortController=new AbortController,this._readyState=l.CLOSED,this.serverReconnectionTime=2e3,this.url=new URL(e.url),this.lastEventId=e.lastEventId,this.headers={Accept:`text/event-stream`,...e.headers},this.requestOptions={body:e.body,method:e.method??`GET`,mode:e.mode,credentials:e.credentials,redirect:e.redirect,referrer:e.referrer,referrerPolicy:e.referrerPolicy},this._onError=this.getValidatedFunctionOrNoOp(e.onError,`onError`),this._onConnect=this.getValidatedFunctionOrNoOp(e.onConnect,`onConnect`),this._onDisconnect=this.getValidatedFunctionOrNoOp(e.onDisconnect,`onDisconnect`),this._onMessage=this.getValidatedFunctionOrNoOp(e.onMessage,`onMessage`),this._onComment=this.getValidatedFunctionOrNoOp(e.onComment,`onComment`),this._shouldReconnect=this.getValidatedFunctionOr(e.shouldReconnect,()=>!0,`shouldReconnect`),this._calculateReconnectDelay=this.getValidatedFunctionOr(e.calculateReconnectDelay,()=>this.serverReconnectionTime,`calculateReconnectDelay`)}set onMessage(e){this._onMessage=this.getValidatedFunctionOrNoOp(e,`onMessage`)}set onComment(e){this._onComment=this.getValidatedFunctionOrNoOp(e,`onComment`)}set onConnect(e){this._onConnect=this.getValidatedFunctionOrNoOp(e,`onConnect`)}set onDisconnect(e){this._onDisconnect=this.getValidatedFunctionOrNoOp(e,`onDisconnect`)}get readyState(){return this._readyState}connect(){this._readyState===l.CLOSED&&(this.reconnectAttempt=0,this.initiateConnection())}initiateConnection(){this.abortController=new AbortController,this._readyState=l.CONNECTING,fetch(this.url,{...this.requestOptions,headers:this.buildHeaders(),signal:this.abortController.signal,cache:`no-store`}).then(this.handleFetchResponse.bind(this)).catch(e=>{if(e.name===`AbortError`||this.abortController.signal.aborted){this.close();return}this._onError(e),this.scheduleReconnect({error:e})})}close(){clearTimeout(this.reconnectTimeout),this._readyState=l.CLOSED,this.abortController.abort()}disconnected(){clearTimeout(this.reconnectTimeout),this._readyState=l.CLOSED,this._onDisconnect()}buildHeaders(){let e=this.lastEventId?{"Last-Event-ID":this.lastEventId}:void 0;return{...this.headers,...e}}async handleFetchResponse(e){let{body:t,status:n}=e;if(n===204){this.disconnected();return}if(n>=400){this.scheduleReconnect({status:n});return}if(!t)throw new a(`The server response did not have a body`);this._readyState=l.OPEN,this._onConnect(),this.reconnectAttempt=0;let r=(t?.pipeThrough(new TextDecoderStream)).getReader(),o=new c({onEvent:e=>{typeof e.id==`string`&&(this.lastEventId=e.id),this._onMessage(e)},onComment:e=>this._onComment(e),onRetry:e=>this.serverReconnectionTime=e}),s=!1;for(;!s;){let{done:e,value:t}=await r.read();t&&o.parse(t),s=e}o.finish(),this.scheduleReconnect({status:n,error:new i(`The server response stream was closed`)})}scheduleReconnect(e){if(this.abortController.signal.aborted)return;clearTimeout(this.reconnectTimeout),this.reconnectAttempt+=1;let t={attempt:this.reconnectAttempt,status:e.status,error:e.error,serverReconnectionTime:this.serverReconnectionTime};if(this._shouldReconnect(t)){this._readyState=l.CONNECTING;let e=this._calculateReconnectDelay(t);(!Number.isFinite(e)||e<1||e>1e3*60*60)&&(this._onError(new s(`The calculated reconnect delay is out of range: ${e}. Defaulting to ${this.serverReconnectionTime}`)),e=this.serverReconnectionTime),this.reconnectTimeout=setTimeout(()=>this.initiateConnection(),e)}else{this.disconnected();return}}getValidatedFunctionOrNoOp(e,t){return this.getValidatedFunctionOr(e,()=>{},t)}getValidatedFunctionOr(e,t,n){if(e==null)return t;if(typeof e==`function`)return e;throw new o(`${n} must be a function`)}};const d=e=>new u(e);var f=class{constructor(t){this.options=t,this.eventEmitter=new e,this.options=t,this.logger=t.logger,this.url=new URL(`client/sse/v1`,t.baseUrl)}async connect(e,t){this.eventSource&&this.close();let n=new Promise((t,n)=>{this.eventSource=d({url:this.url,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:this.options.metaContext,clientSdkKey:this.options.clientSdkKey}),onMessage:({data:e})=>{this.dispatchMessage(e)},onConnect:()=>{this.logger.debug(`[EventSourceTransport] Connected`),t(this)},shouldReconnect:e=>{let t=!this.isStatusFatal(e.status);return t||n(this.prepareFatalError(e.status,e.error)),t},calculateReconnectDelay:e=>{let t=2e3*e.attempt;return this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${t}ms.`),t},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}}),this.eventSource.connect()});return Promise.race([n,new Promise(e=>{setTimeout(()=>e(this),t)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configSetReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,n){let r=e??0;return new t(`${`Connection failed with status: ${e??`unknown`}.`}${n?` Error: ${n}`:``}. This is an unrecoverable error, will not attempt to reconnect.`,r)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){this.eventSource?.close()}dispose(){this.close(),this.clear()}};const p=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},m=e=>e===`number`||e===`bigint`,h=(e,t)=>{let n=e.value,r=p(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=g(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(m(typeof t)&&e.type===`integer`){let e=_(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(m(typeof t)&&(e.type===`float`||e.type===`enum`)){let e=v(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`}},g=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},_=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},v=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},y={off:-1,error:0,warn:1,info:2,debug:3},b=()=>{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 x=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=b()}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){y[this.level]>=y[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},S=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const C=(e,t)=>new x(e??`warn`,t??new S),w=e=>T(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),T=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},E=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},D=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};var O=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){if(!this.executeRequests)return{success:!1,fatalError:!0};let i={clientSdkKey:this.sdkKey,context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(this.isReportEmpty(i))return{success:!0,fatalError:!1};let a=this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return E(e.discreteEvents)&&E(e.aggregatedEvents)&&D(e.droppedEvents)}sendReport(e){try{return{success:navigator.sendBeacon(this.url,JSON.stringify(e)),fatalError:!1}}catch(e){return this.logger.warn(`[EventReporter] Fatal error attempting to send telemetry data: ${e}. No more telemetry data will be sent.`),{success:!1,fatalError:!0}}}},k=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}))}},A=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}},j=class{constructor(e){this.aggregator=new k,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new A(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 w(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()}},M=class extends j{constructor(e){super(e),this.reporter=new O(e);try{document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.flush()})}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(e))}sanitizeEvaluatedConfigEvent(e){return{...e,defaultValue:this.sanitizeValue(e.defaultValue,e.type),evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type)}}};const N=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 P=class{constructor(t){this.options=t,this.eventEmitter=new e,this.fatalError=!1,this.options=t,this.logger=t.logger,this.url=new URL(`client/pull/v1`,t.baseUrl)}async connect(e,n){if(this.fatalError)return this.logger.warn(`[PullTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`),this;try{let r=await N(n,this.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:this.options.metaContext,clientSdkKey:this.options.clientSdkKey})},this.logger);if(!r.ok)throw this.isStatusFatal(r.status)?(this.fatalError=!0,this.prepareFatalResponseStatusError(r.status,await r.text())):new t(`Connection failed with status: ${r.status}`,r.status);let i=JSON.parse(await r.text());return this.eventEmitter.emit(`configSetReceived`,i),this}catch(e){throw r(e)?(this.fatalError=!0,new t(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new t(`Failed to parse the response from the server: ${e}`):new t(`Connection failed with error: ${e}.`)}}prepareFatalResponseStatusError(e,n){let r=e??0;return new t(`${`Connection failed with status: ${e??`unknown`}`}${(n?.trim()?.length??0)>0?` (${n})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,r)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){}dispose(){this.close(),this.clear()}};const F=new URL(`https://client-sdk-api.configdirector.com`);var I=class{constructor(t,n,r){this.handlersMap=new Map,this.eventEmitter=new e,this.ready=!1,this.logger=r?.logger??C(),this.timeout=r?.connection?.timeout??3e3;let i=this.parseUrl(r?.connection?.url)??F;this.streaming=r?.connection?.streaming!==!1;let a=this.streaming?f:P;this.usageEventCollector=new M({sdkKey:t,logger:this.logger,baseUrl:i}),this.transport=new a({clientSdkKey:t,baseUrl:i,metaContext:{...r?.metadata,sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:navigator?.userAgent},logger:this.logger}),this.transport.on(`configSetReceived`,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(e){await this.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}async connectToTransport(e,t){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.eventEmitter.emit(`clientReady`,{action:t}),this.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let n=new Date().getTime();await this.transport.connect(e??{},this.timeout),this.currentContext=e,this.usageEventCollector.updateContext(e);let r=new Date().getTime()-n,i=this.timeout-r;if(i>0&&await Promise.race([this.readyPromise,new Promise(e=>{setTimeout(()=>e(),i)})]),!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 ${t} after ${this.timeout}ms. ${e}`)}}catch(e){this.logger.error(`[ConfigDirectorClient] An error occurred during ${t}: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){this.handlersMap.get(e.key)?.forEach(t=>{let n=this.getValueFromConfigState(e.key,e,t.defaultValue);t.handler(n)})}watch(e,t,n){this.validateDefaultValue(t);let r=this.handlersMap.get(e),i={handler:n,defaultValue:t,requestedType:typeof t};return r?r.push(i):this.handlersMap.set(e,[i]),()=>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){this.validateDefaultValue(t);let n=this.configSet?.configs[e];return this.getValueFromConfigState(e,n,t)}getValueFromConfigState(e,t,n){if(!t)return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({contextId:this.currentContext?.id,key:e,defaultValue:n,requestedType:p(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n;let r=h(t,n);return this.usageEventCollector.evaluatedConfig({contextId:this.currentContext?.id,key:e,defaultValue:n,requestedType:r.requestedType,evaluatedValue:r.parsedValue,usedDefault:r.usedDefault,evaluationReason:r.reason}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${r.parsedValue}'`),r.parsedValue}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new n(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new n(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new n(`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){this.eventEmitter.off(e,t)}clear(){this.logger.debug(`[ConfigDirectorClient] clear() has been called, removing all observers`),this.eventEmitter.clear(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}close(){this.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.clear(),this.close()}};const L=(e,t)=>new I(e,{sdkName:`js-client-sdk`,sdkVersion:`0.2.0`},t),R=(e,t)=>C(e,t);exports.createClient=L,exports.createConsoleLogger=R;
|
|
@@ -50,6 +50,10 @@ type ConfigDirectorContext = {
|
|
|
50
50
|
[key: string]: unknown;
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Metadata about your application. It is recommended you include these values when configuring
|
|
55
|
+
* a ConfigDirector client so that you can use them in targeting rules.
|
|
56
|
+
*/
|
|
53
57
|
type ConfigDirectorMetaContext = {
|
|
54
58
|
appVersion?: string;
|
|
55
59
|
appName?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configdirector-client.d.cts","names":[],"sources":["../../js-client-core/src/Emitter.ts","../../shared/src/types.ts","../../js-client-core/src/types.ts","../../shared/src/logger.ts","../../shared/src/errors.ts","../src/api.ts"],"mappings":";KAAK,SAAA;AAAA,KACA,YAAA,GAAe,MAAA,CAAO,SAAA;AAAA,KAEtB,OAAA,uBAA8B,YAAA,qBAAiC,aAAA,KAClE,OAAA,EAAS,aAAA,CAAc,IAAA;AAAA,UAGR,aAAA,uBAAoC,YAAA;EACnD,EAAA,qBAAuB,aAAA,EAAe,IAAA,EAAM,KAAA,EAAO,OAAA,EAAS,OAAA,CAAQ,aAAA,EAAe,KAAA;EACnF,GAAA,qBAAwB,aAAA,EAAe,IAAA,EAAM,KAAA,EAAO,OAAA,GAAU,OAAA,CAAQ,aAAA,EAAe,KAAA;EACrF,KAAA;AAAA;;;KCEU,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;;;ADlClB;;KCkDY,qBAAA;EDlDyC;;;;;;;;EC2DnD,EAAA;EDzDqF;;;;EC+DrF,IAAA;EDjEmD;;;;ECuEnD,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA
|
|
1
|
+
{"version":3,"file":"configdirector-client.d.cts","names":[],"sources":["../../js-client-core/src/Emitter.ts","../../shared/src/types.ts","../../js-client-core/src/types.ts","../../shared/src/logger.ts","../../shared/src/errors.ts","../src/api.ts"],"mappings":"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"}
|
|
@@ -50,6 +50,10 @@ type ConfigDirectorContext = {
|
|
|
50
50
|
[key: string]: unknown;
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Metadata about your application. It is recommended you include these values when configuring
|
|
55
|
+
* a ConfigDirector client so that you can use them in targeting rules.
|
|
56
|
+
*/
|
|
53
57
|
type ConfigDirectorMetaContext = {
|
|
54
58
|
appVersion?: string;
|
|
55
59
|
appName?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configdirector-client.d.mts","names":[],"sources":["../../js-client-core/src/Emitter.ts","../../shared/src/types.ts","../../js-client-core/src/types.ts","../../shared/src/logger.ts","../../shared/src/errors.ts","../src/api.ts"],"mappings":";KAAK,SAAA;AAAA,KACA,YAAA,GAAe,MAAA,CAAO,SAAA;AAAA,KAEtB,OAAA,uBAA8B,YAAA,qBAAiC,aAAA,KAClE,OAAA,EAAS,aAAA,CAAc,IAAA;AAAA,UAGR,aAAA,uBAAoC,YAAA;EACnD,EAAA,qBAAuB,aAAA,EAAe,IAAA,EAAM,KAAA,EAAO,OAAA,EAAS,OAAA,CAAQ,aAAA,EAAe,KAAA;EACnF,GAAA,qBAAwB,aAAA,EAAe,IAAA,EAAM,KAAA,EAAO,OAAA,GAAU,OAAA,CAAQ,aAAA,EAAe,KAAA;EACrF,KAAA;AAAA;;;KCEU,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;;;ADlClB;;KCkDY,qBAAA;EDlDyC;;;;;;;;EC2DnD,EAAA;EDzDqF;;;;EC+DrF,IAAA;EDjEmD;;;;ECuEnD,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA
|
|
1
|
+
{"version":3,"file":"configdirector-client.d.mts","names":[],"sources":["../../js-client-core/src/Emitter.ts","../../shared/src/types.ts","../../js-client-core/src/types.ts","../../shared/src/logger.ts","../../shared/src/errors.ts","../src/api.ts"],"mappings":"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"}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import{createEventSource as e}from"eventsource-client";var t=class{constructor(){this.handlerMap=new Map}on(e,t){let n=this.handlerMap.get(e);n?n.push(t):this.handlerMap.set(e,[t])}once(e,t){let n=this;function r(i){n.off(e,r),t.apply(n,i)}this.on(e,r)}off(e,t){let n=this.handlerMap.get(e);if(n)if(t){let e=n.indexOf(t);e>=0&&n.splice(e,1)}else this.handlerMap.set(e,[])}clear(){this.handlerMap.clear()}emit(e,t){let n=this.handlerMap.get(e);n&&n.slice().map(e=>e(t))}},n=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},r=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}};const i=e=>{if(e instanceof DOMException){if(e.name===`NotAllowedError`)return!0}else if(e instanceof TypeError)return!0;return!1};var a=class{constructor(e){this.options=e,this.eventEmitter=new t,this.options=e,this.logger=e.logger,this.url=new URL(`client/sse/v1`,e.baseUrl)}async connect(t,n){var r=this;r.eventSource&&r.close();let i,a,o=async(e,t)=>{let n=await fetch(e,t);return i=n.status,n.ok||(a=await n.text()),n},s=new Promise((n,s)=>{r.eventSource=e({url:r.url,fetch:o,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:t,metaContext:r.options.metaContext,clientSdkKey:r.options.clientSdkKey}),onMessage:({data:e})=>{r.dispatchMessage(e)},onConnect:()=>{i&&r.isStatusFatal(i)?(r.close(),s(r.prepareFatalError(i,a))):(r.logger.debug(`[EventSourceTransport] Connected, status: %s`,i),n(r))},onScheduleReconnect:e=>{i&&r.isStatusFatal(i)?(r.close(),s(r.prepareFatalError(i,a))):r.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${e.delay}. Response status: ${i}`)},onDisconnect:()=>{r.logger.debug(`[EventSourceTransport] Disconnected`)}})});return Promise.race([s,new Promise(e=>{setTimeout(()=>e(r),n)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configSetReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,t){var r,i;let a=e==null?0:e;return new n(`${`Connection failed with status: ${e==null?`unknown`:e}`}${((r=t==null||(i=t.trim())==null?void 0:i.length)==null?0:r)>0?` (${t})`:``}. This is an unrecoverable error, will not attempt to reconnect.`,a)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){var e;(e=this.eventSource)==null||e.close()}dispose(){this.close(),this.clear()}};const o=e=>{let t=typeof e;if(t===`object`)try{var n,r;return(n=(r=e.constructor)==null?void 0:r.name)==null?t:n}catch(e){return t}else if(t===`function`)try{let n=e.name;return n?`function: ${n}`:t}catch(e){return t}return t},s=e=>e===`number`||e===`bigint`,c=(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=l(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=u(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=d(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`}},l=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},u=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},d=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t};function f(e){"@babel/helpers - typeof";return f=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},f(e)}function p(e,t){if(f(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(f(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function m(e){var t=p(e,`string`);return f(t)==`symbol`?t:t+``}function h(e,t,n){return(t=m(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function _(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?g(Object(n),!0).forEach(function(t){h(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):g(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}const v={off:-1,error:0,warn:1,info:2,debug:3},y=()=>{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(t){return new Intl.DateTimeFormat(void 0,e)}};var b=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=y()}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){if(v[this.level]>=v[t]){var i;e(`[${this.dateFormatter.format(new Date)}] ${(i=this.decorator)==null?void 0:i.decorateMessage(n)}`,...r)}}},x=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const S=(e,t)=>new b(e==null?`warn`:e,t==null?new x:t),C=e=>w(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),w=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},T=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let i of t){var n,r;if(((n=(r=e[i])==null?void 0:r.length)==null?0:n)>0)return!1}return!0},E=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let r of t){var n;if(((n=e[r])==null?0:n)>0)return!1}return!0};var D=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){var i=this;if(!i.executeRequests)return{success:!1,fatalError:!0};let a={clientSdkKey:i.sdkKey,context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(i.isReportEmpty(a))return{success:!0,fatalError:!1};let o=i.sendReport(a);return o.fatalError&&(i.executeRequests=!1),o}isReportEmpty(e){return T(e.discreteEvents)&&T(e.aggregatedEvents)&&E(e.droppedEvents)}sendReport(e){try{return{success:navigator.sendBeacon(this.url,JSON.stringify(e)),fatalError:!1}}catch(e){return this.logger.warn(`[EventReporter] Fatal error attempting to send telemetry data: ${e}. No more telemetry data will be sent.`),{success:!1,fatalError:!0}}}},O=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let r of e.events){var n;let e=JSON.stringify(r),i=((n=t.get(e))==null?void 0:n.count)||0;t.set(e,{event:r,count:i+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==null?1e3:e}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(){var e;let t=new Date,n=(e=this.startTime)==null?t:e,r=this._events.splice(0),i=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:n,endTime:t,events:r,droppedCount:i}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},A=class{constructor(e){var t,n,r;this.aggregator=new O,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new k((t=e.evaluationQueueLimit)==null?1e3:t),this.flushIntervalDelay=(n=e.flushIntervalDelay)==null?3e4:n;let i=(r=e.initialFlushIntervalDelay)==null?5e3:r;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),i)}sanitizeValue(e,t){if(t===`json`)try{return C(JSON.stringify(e))}catch(t){return e.toString().slice(0,500)}return e.toString().slice(0,500)}async flushAndScheduleNext(){var e=this;(await e.flush()).fatalError?(e.collectEvents=!1,e.close(),e.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):e.flushTimeout=setTimeout(()=>e.flushAndScheduleNext(),e.flushIntervalDelay)}async flush(){var e=this,t;if(!e.reporter)return{success:!1,fatalError:!1};let n=e.evaluationEventQueue.takeSnapshot();return await((t=e.reporter)==null?void 0:t.report({context:e._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:e.aggregator.aggregate(n)},droppedEvents:{evaluatedConfig:n.droppedCount}}))}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},j=class extends A{constructor(e){super(e),this.reporter=new D(e);try{document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.flush()})}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){var t=this;t.cancelScheduledFlush(),await t.flush(),t._context=e,await t.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(e))}sanitizeEvaluatedConfigEvent(e){return _(_({},e),{},{defaultValue:this.sanitizeValue(e.defaultValue,e.type),evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type)})}};const M=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 N=class{constructor(e){this.options=e,this.eventEmitter=new t,this.fatalError=!1,this.options=e,this.logger=e.logger,this.url=new URL(`client/pull/v1`,e.baseUrl)}async connect(e,t){var r=this;if(r.fatalError)return r.logger.warn(`[PullTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`),r;try{let i=await M(t,r.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:r.options.metaContext,clientSdkKey:r.options.clientSdkKey})},r.logger);if(!i.ok)throw r.isStatusFatal(i.status)?(r.fatalError=!0,r.prepareFatalResponseStatusError(i.status,await i.text())):new n(`Connection failed with status: ${i.status}`,i.status);let a=JSON.parse(await i.text());return r.eventEmitter.emit(`configSetReceived`,a),r}catch(e){throw i(e)?(r.fatalError=!0,new n(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new n(`Failed to parse the response from the server: ${e}`):new n(`Connection failed with error: ${e}.`)}}prepareFatalResponseStatusError(e,t){var r,i;let a=e==null?0:e;return new n(`${`Connection failed with status: ${e==null?`unknown`:e}`}${((r=t==null||(i=t.trim())==null?void 0:i.length)==null?0:r)>0?` (${t})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,a)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){}dispose(){this.close(),this.clear()}};const P=new URL(`https://client-sdk-api.configdirector.com`);var F=class{constructor(e,n,r){var i,o,s,c,l,u,d;this.handlersMap=new Map,this.eventEmitter=new t,this.ready=!1,this.logger=(i=r==null?void 0:r.logger)==null?S():i,this.timeout=(o=r==null||(s=r.connection)==null?void 0:s.timeout)==null?3e3:o;let f=(c=this.parseUrl(r==null||(l=r.connection)==null?void 0:l.url))==null?P:c;this.streaming=(r==null||(u=r.connection)==null?void 0:u.streaming)!==!1;let p=this.streaming?a:N;this.usageEventCollector=new j({sdkKey:e,logger:this.logger,baseUrl:f}),this.transport=new p({clientSdkKey:e,baseUrl:f,metaContext:_(_({},r==null?void 0:r.metadata),{},{sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:(d=navigator)==null?void 0:d.userAgent}),logger:this.logger}),this.transport.on(`configSetReceived`,e=>{var t;(t=this.readyResolve)==null||t.call(this);let n=Object.keys(e.configs);!this.configSet||e.kind==`full`?(this.configSet=e,this.eventEmitter.emit(`configsUpdated`,{keys:n}),this.updateWatchers(e.configs)):(this.configSet.configs=_(_({},this.configSet.configs),e.configs),this.eventEmitter.emit(`configsUpdated`,{keys:n}),this.updateWatchers(e.configs)),this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server:`,{keys:n})})}async initialize(e){await this.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}async connectToTransport(e,t){var n=this;try{n.ready=!1,n.readyPromise=new Promise(e=>{n.readyResolve=e}).then(()=>{n.ready=!0,n.eventEmitter.emit(`clientReady`,{action:t}),n.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let r=new Date().getTime();await n.transport.connect(e==null?{}:e,n.timeout),n.currentContext=e,n.usageEventCollector.updateContext(e);let i=new Date().getTime()-r,a=n.timeout-i;if(a>0&&await Promise.race([n.readyPromise,new Promise(e=>{setTimeout(()=>e(),a)})]),!n.ready){let e=n.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.`;n.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${t} after ${n.timeout}ms. ${e}`)}}catch(e){n.logger.error(`[ConfigDirectorClient] An error occurred during ${t}: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){var t;(t=this.handlersMap.get(e.key))==null||t.forEach(t=>{let n=this.getValueFromConfigState(e.key,e,t.defaultValue);t.handler(n)})}watch(e,t,n){this.validateDefaultValue(t);let r=this.handlersMap.get(e),i={handler:n,defaultValue:t,requestedType:typeof t};return r?r.push(i):this.handlersMap.set(e,[i]),()=>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==null||n.splice(e,1))}else n.splice(0)}getValue(e,t){var n;this.validateDefaultValue(t);let r=(n=this.configSet)==null?void 0:n.configs[e];return this.getValueFromConfigState(e,r,t)}getValueFromConfigState(e,t,n){var r;if(!t){var i;return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({contextId:(i=this.currentContext)==null?void 0:i.id,key:e,defaultValue:n,requestedType:o(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n}let a=c(t,n);return this.usageEventCollector.evaluatedConfig({contextId:(r=this.currentContext)==null?void 0:r.id,key:e,defaultValue:n,requestedType:a.requestedType,evaluatedValue:a.parsedValue,usedDefault:a.usedDefault,evaluationReason:a.reason}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${a.parsedValue}'`),a.parsedValue}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new r(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new r(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new r(`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){this.eventEmitter.off(e,t)}clear(){this.logger.debug(`[ConfigDirectorClient] clear() has been called, removing all observers`),this.eventEmitter.clear(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}close(){this.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.clear(),this.close()}};const I=(e,t)=>new F(e,{sdkName:`js-client-sdk`,sdkVersion:`0.1.10`},t),L=(e,t)=>S(e,t);export{I as createClient,L as createConsoleLogger};
|
|
1
|
+
var e=class{constructor(){this.handlerMap=new Map}on(e,t){let n=this.handlerMap.get(e);n?n.push(t):this.handlerMap.set(e,[t])}once(e,t){let n=this;function r(i){n.off(e,r),t.apply(n,i)}this.on(e,r)}off(e,t){let n=this.handlerMap.get(e);if(n)if(t){let e=n.indexOf(t);e>=0&&n.splice(e,1)}else this.handlerMap.set(e,[])}clear(){this.handlerMap.clear()}emit(e,t){let n=this.handlerMap.get(e);n&&n.slice().map(e=>e(t))}},t=class e extends Error{constructor(t,n){super(t),this.name=`ConfigDirectorConnectionError`,this.status=n,Object.setPrototypeOf(this,e.prototype)}},n=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorValidationError`,Object.setPrototypeOf(this,e.prototype)}};const r=e=>{if(e instanceof DOMException){if(e.name===`NotAllowedError`)return!0}else if(e instanceof TypeError)return!0;return!1};var i=class e extends Error{constructor(t){super(t),this.name=`StreamClosedError`,Object.setPrototypeOf(this,e.prototype)}},a=class e extends Error{constructor(t){super(t),this.name=`MissingResponseBodyError`,Object.setPrototypeOf(this,e.prototype)}},o=class e extends Error{constructor(t){super(t),this.name=`InvalidOptionError`,Object.setPrototypeOf(this,e.prototype)}},s=class e extends Error{constructor(t){super(t),this.name=`ValueOutOfRangeError`,Object.setPrototypeOf(this,e.prototype)}};function c(e){"@babel/helpers - typeof";return c=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},c(e)}function l(e,t){if(c(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(c(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function u(e){var t=l(e,`string`);return c(t)==`symbol`?t:t+``}function d(e,t,n){return(t=u(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function f(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function p(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?f(Object(n),!0).forEach(function(t){d(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):f(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}var m=class{constructor(e){var t;this.isFirstChunk=!0,this.previousIncompleteLine=``,this.currentEvent={data:``},this.onEvent=(t=e.onEvent)==null?(()=>{}):t,this.onRetry=e.onRetry,this.onComment=e.onComment}parse(e){let t=this.prepareChunk(e),n=this.splitLines(t);for(let e of n)this.parseAndProcessLine(e)}finish(){this.previousIncompleteLine!==``&&this.parseAndProcessLine(this.previousIncompleteLine)}parseAndProcessLine(e){if(e.startsWith(`:`)){var t;(t=this.onComment)==null||t.call(this,this.getValueFromLine(0,e));return}if(e===``){this.publishEvent();return}let n=this.parseContentLine(e);this.processContent(n)}publishEvent(){let e=this.currentEvent.data.endsWith(`
|
|
2
|
+
`)?this.currentEvent.data.slice(0,-1):this.currentEvent.data;e&&this.onEvent(p(p({},this.currentEvent),{},{id:this.lastEventId,data:e})),this.currentEvent={data:``}}processContent(e){switch(e.field){case`event`:this.currentEvent.type=e.value;break;case`data`:this.currentEvent.data+=e.value+`
|
|
3
|
+
`;break;case`id`:e.value.includes(`\0`)||(this.lastEventId=e.value);break;case`retry`:if(/^\d+$/.test(e.value)){var t;(t=this.onRetry)==null||t.call(this,Number.parseInt(e.value,10))}break;default:break}}parseContentLine(e){let t=e.indexOf(`:`);return t===-1?{field:e,value:``}:{field:e.slice(0,t),value:this.getValueFromLine(t,e)}}splitLines(e){var t;let n=e.split(/\r\n|\r|\n/);return this.previousIncompleteLine=(t=n.pop())==null?``:t,n}getValueFromLine(e,t){if(e===-1)return``;let n=t[e+1]===` `?e+2:e+1;return t.slice(n)}prepareChunk(e){return this.isFirstChunk?(this.isFirstChunk=!1,this.previousIncompleteLine+e.replace(/^\xEF\xBB\xBF/,``).replace(/^\uFEFF/,``)):this.previousIncompleteLine+e}};let h=function(e){return e.OPEN=`open`,e.CLOSED=`closed`,e.CONNECTING=`connecting`,e}({});var g=class{constructor(e){var t;this.reconnectAttempt=0,this.abortController=new AbortController,this._readyState=h.CLOSED,this.serverReconnectionTime=2e3,this.url=new URL(e.url),this.lastEventId=e.lastEventId,this.headers=p({Accept:`text/event-stream`},e.headers),this.requestOptions={body:e.body,method:(t=e.method)==null?`GET`:t,mode:e.mode,credentials:e.credentials,redirect:e.redirect,referrer:e.referrer,referrerPolicy:e.referrerPolicy},this._onError=this.getValidatedFunctionOrNoOp(e.onError,`onError`),this._onConnect=this.getValidatedFunctionOrNoOp(e.onConnect,`onConnect`),this._onDisconnect=this.getValidatedFunctionOrNoOp(e.onDisconnect,`onDisconnect`),this._onMessage=this.getValidatedFunctionOrNoOp(e.onMessage,`onMessage`),this._onComment=this.getValidatedFunctionOrNoOp(e.onComment,`onComment`),this._shouldReconnect=this.getValidatedFunctionOr(e.shouldReconnect,()=>!0,`shouldReconnect`),this._calculateReconnectDelay=this.getValidatedFunctionOr(e.calculateReconnectDelay,()=>this.serverReconnectionTime,`calculateReconnectDelay`)}set onMessage(e){this._onMessage=this.getValidatedFunctionOrNoOp(e,`onMessage`)}set onComment(e){this._onComment=this.getValidatedFunctionOrNoOp(e,`onComment`)}set onConnect(e){this._onConnect=this.getValidatedFunctionOrNoOp(e,`onConnect`)}set onDisconnect(e){this._onDisconnect=this.getValidatedFunctionOrNoOp(e,`onDisconnect`)}get readyState(){return this._readyState}connect(){this._readyState===h.CLOSED&&(this.reconnectAttempt=0,this.initiateConnection())}initiateConnection(){this.abortController=new AbortController,this._readyState=h.CONNECTING,fetch(this.url,p(p({},this.requestOptions),{},{headers:this.buildHeaders(),signal:this.abortController.signal,cache:`no-store`})).then(this.handleFetchResponse.bind(this)).catch(e=>{if(e.name===`AbortError`||this.abortController.signal.aborted){this.close();return}this._onError(e),this.scheduleReconnect({error:e})})}close(){clearTimeout(this.reconnectTimeout),this._readyState=h.CLOSED,this.abortController.abort()}disconnected(){clearTimeout(this.reconnectTimeout),this._readyState=h.CLOSED,this._onDisconnect()}buildHeaders(){let e=this.lastEventId?{"Last-Event-ID":this.lastEventId}:void 0;return p(p({},this.headers),e)}async handleFetchResponse(e){var t=this;let{body:n,status:r}=e;if(r===204){t.disconnected();return}if(r>=400){t.scheduleReconnect({status:r});return}if(!n)throw new a(`The server response did not have a body`);t._readyState=h.OPEN,t._onConnect(),t.reconnectAttempt=0;let o=(n==null?void 0:n.pipeThrough(new TextDecoderStream)).getReader(),s=new m({onEvent:e=>{typeof e.id==`string`&&(t.lastEventId=e.id),t._onMessage(e)},onComment:e=>t._onComment(e),onRetry:e=>t.serverReconnectionTime=e}),c=!1;for(;!c;){let{done:e,value:t}=await o.read();t&&s.parse(t),c=e}s.finish(),t.scheduleReconnect({status:r,error:new i(`The server response stream was closed`)})}scheduleReconnect(e){if(this.abortController.signal.aborted)return;clearTimeout(this.reconnectTimeout),this.reconnectAttempt+=1;let t={attempt:this.reconnectAttempt,status:e.status,error:e.error,serverReconnectionTime:this.serverReconnectionTime};if(this._shouldReconnect(t)){this._readyState=h.CONNECTING;let e=this._calculateReconnectDelay(t);(!Number.isFinite(e)||e<1||e>1e3*60*60)&&(this._onError(new s(`The calculated reconnect delay is out of range: ${e}. Defaulting to ${this.serverReconnectionTime}`)),e=this.serverReconnectionTime),this.reconnectTimeout=setTimeout(()=>this.initiateConnection(),e)}else{this.disconnected();return}}getValidatedFunctionOrNoOp(e,t){return this.getValidatedFunctionOr(e,()=>{},t)}getValidatedFunctionOr(e,t,n){if(e==null)return t;if(typeof e==`function`)return e;throw new o(`${n} must be a function`)}};const _=e=>new g(e);var v=class{constructor(t){this.options=t,this.eventEmitter=new e,this.options=t,this.logger=t.logger,this.url=new URL(`client/sse/v1`,t.baseUrl)}async connect(e,t){var n=this;n.eventSource&&n.close();let r=new Promise((t,r)=>{n.eventSource=_({url:n.url,method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:n.options.metaContext,clientSdkKey:n.options.clientSdkKey}),onMessage:({data:e})=>{n.dispatchMessage(e)},onConnect:()=>{n.logger.debug(`[EventSourceTransport] Connected`),t(n)},shouldReconnect:e=>{let t=!n.isStatusFatal(e.status);return t||r(n.prepareFatalError(e.status,e.error)),t},calculateReconnectDelay:e=>{let t=2e3*e.attempt;return n.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${t}ms.`),t},onDisconnect:()=>{n.logger.debug(`[EventSourceTransport] Disconnected`)}}),n.eventSource.connect()});return Promise.race([r,new Promise(e=>{setTimeout(()=>e(n),t)})])}dispatchMessage(e){try{let t=JSON.parse(e);this.eventEmitter.emit(`configSetReceived`,t)}catch(e){this.logger.error(`[EventSourceTransport] Error parsing and dispatching config data update: `,e)}}prepareFatalError(e,n){let r=e==null?0:e;return new t(`${`Connection failed with status: ${e==null?`unknown`:e}.`}${n?` Error: ${n}`:``}. This is an unrecoverable error, will not attempt to reconnect.`,r)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){var e;(e=this.eventSource)==null||e.close()}dispose(){this.close(),this.clear()}};const y=e=>{let t=typeof e;if(t===`object`)try{var n,r;return(n=(r=e.constructor)==null?void 0:r.name)==null?t:n}catch(e){return t}else if(t===`function`)try{let n=e.name;return n?`function: ${n}`:t}catch(e){return t}return t},b=e=>e===`number`||e===`bigint`,x=(e,t)=>{let n=e.value,r=y(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=S(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(b(typeof t)&&e.type===`integer`){let e=C(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(b(typeof t)&&(e.type===`float`||e.type===`enum`)){let e=w(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`}},S=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},C=e=>{if(!e)return;let t=Number.parseInt(e);if(!(isNaN(t)||!isFinite(t)))return t},w=e=>{if(!e)return;let t=Number.parseFloat(e);if(!(isNaN(t)||!isFinite(t)))return t},T={off:-1,error:0,warn:1,info:2,debug:3},E=()=>{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,p(p({},e),{},{fractionalSecondDigits:3}))}catch(t){return new Intl.DateTimeFormat(void 0,e)}};var D=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=E()}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){if(T[this.level]>=T[t]){var i;e(`[${this.dateFormatter.format(new Date)}] ${(i=this.decorator)==null?void 0:i.decorateMessage(n)}`,...r)}}},O=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const k=(e,t)=>new D(e==null?`warn`:e,t==null?new O:t),A=e=>j(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),j=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e[n],t>>>=0;return t},M=e=>{let t=Object.keys(e);if(t.length==0)return!0;for(let i of t){var n,r;if(((n=(r=e[i])==null?void 0:r.length)==null?0:n)>0)return!1}return!0},N=e=>{if(!e)return!0;let t=Object.keys(e);if(t.length==0)return!0;for(let r of t){var n;if(((n=e[r])==null?0:n)>0)return!1}return!0};var P=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=new URL(`client/telemetry/v1`,e.baseUrl)}async report({context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r}){var i=this;if(!i.executeRequests)return{success:!1,fatalError:!0};let a={clientSdkKey:i.sdkKey,context:e,discreteEvents:t,aggregatedEvents:n,droppedEvents:r};if(i.isReportEmpty(a))return{success:!0,fatalError:!1};let o=i.sendReport(a);return o.fatalError&&(i.executeRequests=!1),o}isReportEmpty(e){return M(e.discreteEvents)&&M(e.aggregatedEvents)&&N(e.droppedEvents)}sendReport(e){try{return{success:navigator.sendBeacon(this.url,JSON.stringify(e)),fatalError:!1}}catch(e){return this.logger.warn(`[EventReporter] Fatal error attempting to send telemetry data: ${e}. No more telemetry data will be sent.`),{success:!1,fatalError:!0}}}},F=class{aggregate(e){if(e.events.length==0)return[];let t=new Map;for(let r of e.events){var n;let e=JSON.stringify(r),i=((n=t.get(e))==null?void 0:n.count)||0;t.set(e,{event:r,count:i+1})}return Array.from(t).map(([,t])=>({startTime:e.startTime,endTime:e.endTime,count:t.count,event:t.event}))}},I=class{constructor(e){this._events=[],this._droppedEventCount=0,this.limit=e==null?1e3:e}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(){var e;let t=new Date,n=(e=this.startTime)==null?t:e,r=this._events.splice(0),i=this._droppedEventCount;return this.startTime=void 0,this._droppedEventCount=0,{startTime:n,endTime:t,events:r,droppedCount:i}}clear(){this._events=[],this.startTime=void 0,this._droppedEventCount=0}},L=class{constructor(e){var t,n,r;this.aggregator=new F,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new I((t=e.evaluationQueueLimit)==null?1e3:t),this.flushIntervalDelay=(n=e.flushIntervalDelay)==null?3e4:n;let i=(r=e.initialFlushIntervalDelay)==null?5e3:r;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),i)}sanitizeValue(e,t){if(t===`json`)try{return A(JSON.stringify(e))}catch(t){return e.toString().slice(0,500)}return e.toString().slice(0,500)}async flushAndScheduleNext(){var e=this;(await e.flush()).fatalError?(e.collectEvents=!1,e.close(),e.logger.warn(`[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.`)):e.flushTimeout=setTimeout(()=>e.flushAndScheduleNext(),e.flushIntervalDelay)}async flush(){var e=this,t;if(!e.reporter)return{success:!1,fatalError:!1};let n=e.evaluationEventQueue.takeSnapshot();return await((t=e.reporter)==null?void 0:t.report({context:e._context,discreteEvents:{},aggregatedEvents:{evaluatedConfig:e.aggregator.aggregate(n)},droppedEvents:{evaluatedConfig:n.droppedCount}}))}cancelScheduledFlush(){clearTimeout(this.flushTimeout)}close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},R=class extends L{constructor(e){super(e),this.reporter=new P(e);try{document.addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&this.flush()})}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){var t=this;t.cancelScheduledFlush(),await t.flush(),t._context=e,await t.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(e))}sanitizeEvaluatedConfigEvent(e){return p(p({},e),{},{defaultValue:this.sanitizeValue(e.defaultValue,e.type),evaluatedValue:this.sanitizeValue(e.evaluatedValue,e.type)})}};const z=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,p(p({},n),{},{signal:i.signal}));return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var B=class{constructor(t){this.options=t,this.eventEmitter=new e,this.fatalError=!1,this.options=t,this.logger=t.logger,this.url=new URL(`client/pull/v1`,t.baseUrl)}async connect(e,n){var i=this;if(i.fatalError)return i.logger.warn(`[PullTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`),i;try{let r=await z(n,i.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:i.options.metaContext,clientSdkKey:i.options.clientSdkKey})},i.logger);if(!r.ok)throw i.isStatusFatal(r.status)?(i.fatalError=!0,i.prepareFatalResponseStatusError(r.status,await r.text())):new t(`Connection failed with status: ${r.status}`,r.status);let a=JSON.parse(await r.text());return i.eventEmitter.emit(`configSetReceived`,a),i}catch(e){throw r(e)?(i.fatalError=!0,new t(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new t(`Failed to parse the response from the server: ${e}`):new t(`Connection failed with error: ${e}.`)}}prepareFatalResponseStatusError(e,n){var r,i;let a=e==null?0:e;return new t(`${`Connection failed with status: ${e==null?`unknown`:e}`}${((r=n==null||(i=n.trim())==null?void 0:i.length)==null?0:r)>0?` (${n})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,a)}isStatusFatal(e){return!!e&&e>=400&&e<500}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}close(){}dispose(){this.close(),this.clear()}};const V=new URL(`https://client-sdk-api.configdirector.com`);var H=class{constructor(t,n,r){var i,a,o,s,c,l,u;this.handlersMap=new Map,this.eventEmitter=new e,this.ready=!1,this.logger=(i=r==null?void 0:r.logger)==null?k():i,this.timeout=(a=r==null||(o=r.connection)==null?void 0:o.timeout)==null?3e3:a;let d=(s=this.parseUrl(r==null||(c=r.connection)==null?void 0:c.url))==null?V:s;this.streaming=(r==null||(l=r.connection)==null?void 0:l.streaming)!==!1;let f=this.streaming?v:B;this.usageEventCollector=new R({sdkKey:t,logger:this.logger,baseUrl:d}),this.transport=new f({clientSdkKey:t,baseUrl:d,metaContext:p(p({},r==null?void 0:r.metadata),{},{sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:(u=navigator)==null?void 0:u.userAgent}),logger:this.logger}),this.transport.on(`configSetReceived`,e=>{var t;(t=this.readyResolve)==null||t.call(this);let n=Object.keys(e.configs);!this.configSet||e.kind==`full`?(this.configSet=e,this.eventEmitter.emit(`configsUpdated`,{keys:n}),this.updateWatchers(e.configs)):(this.configSet.configs=p(p({},this.configSet.configs),e.configs),this.eventEmitter.emit(`configsUpdated`,{keys:n}),this.updateWatchers(e.configs)),this.logger.debug(`[ConfigDirectorClient] ConfigSet updated from server:`,{keys:n})})}async initialize(e){await this.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}async connectToTransport(e,t){var n=this;try{n.ready=!1,n.readyPromise=new Promise(e=>{n.readyResolve=e}).then(()=>{n.ready=!0,n.eventEmitter.emit(`clientReady`,{action:t}),n.logger.debug(`[ConfigDirectorClient] Received initial payload from the server, client is ready`)});let r=new Date().getTime();await n.transport.connect(e==null?{}:e,n.timeout),n.currentContext=e,n.usageEventCollector.updateContext(e);let i=new Date().getTime()-r,a=n.timeout-i;if(a>0&&await Promise.race([n.readyPromise,new Promise(e=>{setTimeout(()=>e(),a)})]),!n.ready){let e=n.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.`;n.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${t} after ${n.timeout}ms. ${e}`)}}catch(e){n.logger.error(`[ConfigDirectorClient] An error occurred during ${t}: `,e)}}updateWatchers(e){Object.values(e).forEach(e=>this.updateWatchersForConfig(e))}updateWatchersForConfig(e){var t;(t=this.handlersMap.get(e.key))==null||t.forEach(t=>{let n=this.getValueFromConfigState(e.key,e,t.defaultValue);t.handler(n)})}watch(e,t,n){this.validateDefaultValue(t);let r=this.handlersMap.get(e),i={handler:n,defaultValue:t,requestedType:typeof t};return r?r.push(i):this.handlersMap.set(e,[i]),()=>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==null||n.splice(e,1))}else n.splice(0)}getValue(e,t){var n;this.validateDefaultValue(t);let r=(n=this.configSet)==null?void 0:n.configs[e];return this.getValueFromConfigState(e,r,t)}getValueFromConfigState(e,t,n){var r;if(!t){var i;return this.logger.debug(`[ConfigDirectorClient] No config state found for '${e}', returning default value '${n}'`),this.usageEventCollector.evaluatedConfig({contextId:(i=this.currentContext)==null?void 0:i.id,key:e,defaultValue:n,requestedType:y(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n}let a=x(t,n);return this.usageEventCollector.evaluatedConfig({contextId:(r=this.currentContext)==null?void 0:r.id,key:e,defaultValue:n,requestedType:a.requestedType,evaluatedValue:a.parsedValue,usedDefault:a.usedDefault,evaluationReason:a.reason}),this.logger.debug(`[ConfigDirectorClient] Evaluated '${e}' to '${a.parsedValue}'`),a.parsedValue}parseUrl(e){if(e)try{return new URL(e)}catch(t){throw new n(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new n(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new n(`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){this.eventEmitter.off(e,t)}clear(){this.logger.debug(`[ConfigDirectorClient] clear() has been called, removing all observers`),this.eventEmitter.clear(),this.handlersMap.clear()}unwatchAll(){this.handlersMap.clear()}close(){this.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),this.transport.close(),this.ready=!1}dispose(){this.clear(),this.close()}};const U=(e,t)=>new H(e,{sdkName:`js-client-sdk`,sdkVersion:`0.2.0`},t),W=(e,t)=>k(e,t);export{U as createClient,W as createConsoleLogger};
|
|
2
4
|
//# sourceMappingURL=configdirector-client.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configdirector-client.mjs","names":["this","this","this","this","this","this"],"sources":["../../js-client-core/src/Emitter.ts","../../shared/src/errors.ts","../../js-client-core/src/errors.ts","../../js-client-core/src/StreamingTransport.ts","../../shared/src/value-parser.ts","../../shared/src/logger.ts","../../js-client-core/src/logger.ts","../../shared/src/telemetry/utils.ts","../../js-client-core/src/telemetry/ClientEventReporter.ts","../../shared/src/telemetry/EventAggregator.ts","../../shared/src/telemetry/EventQueue.ts","../../shared/src/telemetry/TelemetryEventCollector.ts","../../js-client-core/src/telemetry/ClientTelemetryEventCollector.ts","../../shared/src/fetchWithTimeout.ts","../../js-client-core/src/PullTransport.ts","../../js-client-core/src/DefaultConfigDirectorClient.ts","../src/api.ts"],"sourcesContent":["type EventType = string | symbol;\ntype EventsRecord = Record<EventType, any>;\n\ntype Handler<TEventsRecord extends EventsRecord, TKey extends keyof TEventsRecord> = (\n payload: TEventsRecord[TKey],\n) => void;\n\nexport interface EventProvider<TEventsRecord extends EventsRecord> {\n on<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>): void;\n off<TName extends keyof TEventsRecord>(name: TName, handler?: Handler<TEventsRecord, TName>): void;\n clear(): void;\n}\n\nexport class Emitter<TEventsRecord extends EventsRecord> implements EventProvider<TEventsRecord> {\n private handlerMap: Map<keyof TEventsRecord, Array<(payload: any) => void>> = new Map();\n\n on<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>) {\n const handlers = this.handlerMap.get(name);\n if (handlers) {\n handlers.push(handler);\n } else {\n this.handlerMap.set(name, [handler]);\n }\n }\n\n once<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>) {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n function onceHandler(payload: TEventsRecord[keyof TEventsRecord]) {\n self.off(name, onceHandler);\n handler.apply(self, payload);\n }\n this.on(name, onceHandler);\n }\n\n off<TName extends keyof TEventsRecord>(name: TName, handler?: Handler<TEventsRecord, TName>) {\n const handlers = this.handlerMap.get(name);\n if (!handlers) {\n return;\n }\n\n if (handler) {\n const listenerIndex = handlers.indexOf(handler);\n if (listenerIndex >= 0) {\n handlers.splice(listenerIndex, 1);\n }\n } else {\n this.handlerMap.set(name, []);\n }\n }\n\n clear() {\n this.handlerMap.clear();\n }\n\n emit<TName extends keyof TEventsRecord>(name: TName, payload: TEventsRecord[TName]) {\n const handlers = this.handlerMap.get(name);\n if (!handlers) {\n return;\n }\n\n handlers.slice().map((h) => h(payload));\n }\n}\n","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","export { ConfigDirectorConnectionError, ConfigDirectorValidationError } from \"../../shared/src/errors\";\n\nexport const isFetchErrorFatal = (fetchError: any): boolean => {\n if (fetchError instanceof DOMException) {\n const domError = fetchError as DOMException;\n if (domError.name === \"NotAllowedError\") {\n return true;\n }\n } else if (fetchError instanceof TypeError) {\n return true;\n }\n\n return false;\n};\n","import { createEventSource, type EventSourceClient } from \"eventsource-client\";\nimport type {\n ConfigDirectorContext,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { Emitter } from \"./Emitter\";\nimport { ConfigDirectorConnectionError } from \"./errors\";\n\nexport class StreamingTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventSource: EventSourceClient | undefined;\n private eventEmitter = new Emitter<TransportEvents>();\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(\"client/sse/v1\", options.baseUrl);\n }\n\n public async connect(context: ConfigDirectorContext, 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 givenContext: context,\n metaContext: this.options.metaContext,\n clientSdkKey: this.options.clientSdkKey,\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(\"configSetReceived\", json);\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 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 this.eventEmitter.off(name, handler);\n }\n\n public clear(): void {\n this.eventEmitter.clear();\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 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 { ConfigDirectorLogMessageDecorator, ConfigDirectorLoggingLevel } from \"./types\";\nimport { DefaultConsoleLogger } from \"../../shared/src/logger\";\nexport { DefaultConsoleLogger } from \"../../shared/src/logger\";\n\nclass LogMessageDecorator implements ConfigDirectorLogMessageDecorator {\n decorateMessage(message: string): string {\n return `[ConfigDirector:js-client-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 { isDroppedEventsEmpty, isEventListEmpty } from \"@shared/telemetry/utils\";\nimport { ConfigDirectorContext, ConfigDirectorLogger } from \"../types\";\nimport {\n AggregatedEventList,\n DiscreteEventList,\n DroppedEvents,\n EventReport,\n EventReporter,\n ReporterResponse,\n} from \"./types\";\n\nexport type EventReporterOptions = {\n sdkKey: string;\n logger: ConfigDirectorLogger;\n baseUrl: URL;\n};\n\nexport class ClientEventReporter 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(\"client/telemetry/v1\", options.baseUrl);\n }\n\n public async report({\n context,\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n }: {\n context?: ConfigDirectorContext | undefined;\n discreteEvents: DiscreteEventList;\n aggregatedEvents: AggregatedEventList;\n droppedEvents?: DroppedEvents;\n }): Promise<ReporterResponse> {\n if (!this.executeRequests) {\n return { success: false, fatalError: true };\n }\n\n const eventReport: EventReport = {\n clientSdkKey: this.sdkKey,\n context,\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n };\n if (this.isReportEmpty(eventReport)) {\n return { success: true, fatalError: false };\n }\n\n const response = 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 sendReport(eventReport: EventReport): ReporterResponse {\n try {\n const result = navigator.sendBeacon(this.url, JSON.stringify(eventReport));\n return { success: result, fatalError: false };\n } catch (beaconError) {\n this.logger.warn(\n `[EventReporter] Fatal error attempting to send telemetry data: ${beaconError}. No more telemetry data will be sent.`,\n );\n return { success: false, fatalError: true };\n }\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","import { ConfigDirectorContext, ConfigValueType } from \"../types\";\nimport { ClientEventReporter } from \"./ClientEventReporter\";\nimport { EvaluatedConfigEvent } from \"./telemetry-events\";\nimport { EventReporter } from \"./types\";\nimport { TelemetryEventCollector, TelemetryEventCollectorOptions } from \"@shared/telemetry/TelemetryEventCollector\";\n\nexport class ClientTelemetryEventCollector 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 ClientEventReporter(options);\n try {\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.flush();\n }\n });\n } catch (error) {\n this.logger.warn(\"[TelemetryEventCollector] Could not configure 'visibilitychange' listener: \", error);\n }\n }\n\n public async updateContext(value: ConfigDirectorContext | undefined) {\n this.cancelScheduledFlush();\n await this.flush();\n this._context = value;\n await this.flushAndScheduleNext();\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 ...event,\n defaultValue: this.sanitizeValue(event.defaultValue, event.type),\n evaluatedValue: this.sanitizeValue(event.evaluatedValue, event.type),\n };\n }\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 type {\n ConfigDirectorContext,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { Emitter } from \"./Emitter\";\nimport { ConfigDirectorConnectionError, isFetchErrorFatal } from \"./errors\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\n\nexport class PullTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventEmitter = new Emitter<TransportEvents>();\n private url: URL;\n private fatalError = false;\n\n constructor(private readonly options: TransportOptions) {\n this.options = options;\n this.logger = options.logger;\n this.url = new URL(\"client/pull/v1\", options.baseUrl);\n }\n\n public async connect(context: ConfigDirectorContext, timeout: number): Promise<this> {\n if (this.fatalError) {\n this.logger.warn(\n \"[PullTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.\",\n );\n return this;\n }\n\n try {\n const response = await fetchWithTimeout(timeout, this.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n givenContext: context,\n metaContext: this.options.metaContext,\n clientSdkKey: this.options.clientSdkKey,\n }),\n }, this.logger);\n\n if (!response.ok) {\n if (this.isStatusFatal(response.status)) {\n this.fatalError = true;\n throw this.prepareFatalResponseStatusError(response.status, await response.text());\n } else {\n throw new ConfigDirectorConnectionError(\n `Connection failed with status: ${response.status}`,\n response.status,\n );\n }\n }\n\n const json = JSON.parse(await response.text());\n this.eventEmitter.emit(\"configSetReceived\", json);\n return this;\n } catch (fetchError) {\n if (isFetchErrorFatal(fetchError)) {\n this.fatalError = true;\n throw new ConfigDirectorConnectionError(\n `Connection failed with fatal error: ${fetchError}. This is an unrecoverable error, retry attempts will be ignored.`,\n );\n } else if (fetchError instanceof SyntaxError) {\n throw new ConfigDirectorConnectionError(\n `Failed to parse the response from the server: ${fetchError}`,\n );\n } else {\n throw new ConfigDirectorConnectionError(`Connection failed with error: ${fetchError}.`);\n }\n }\n }\n\n private prepareFatalResponseStatusError(\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, retry attempts will be ignored.`;\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 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 this.eventEmitter.off(name, handler);\n }\n\n public clear(): void {\n this.eventEmitter.clear();\n }\n\n public close() {}\n\n public dispose(): void {\n this.close();\n this.clear();\n }\n}\n","import { Emitter } from \"./Emitter\";\nimport { StreamingTransport } from \"./StreamingTransport\";\nimport { getRequestedType, parseConfigValue } from \"../../shared/src/value-parser\";\nimport {\n ConfigSet,\n ConfigState,\n ConfigStateMap,\n ConfigDirectorContext,\n ConfigDirectorClientOptions,\n ConfigDirectorClient,\n ClientEvents,\n WatchHandler,\n ConfigValueType,\n ConfigDirectorLogger,\n Transport,\n ClientConnectAction,\n IdentifyingSdkOptions,\n} from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\nimport { ClientTelemetryEventCollector } from \"./telemetry\";\nimport { ConfigDirectorValidationError } from \"./errors\";\nimport { PullTransport } from \"./PullTransport\";\n\nconst defaultBaseUrl = new URL(\"https://client-sdk-api.configdirector.com\");\n\ntype WatchHandlerWithOptions<T extends ConfigValueType> = {\n handler: WatchHandler<T>;\n defaultValue: T;\n requestedType: string;\n};\n\nexport class DefaultConfigDirectorClient implements ConfigDirectorClient {\n private logger: ConfigDirectorLogger;\n private usageEventCollector: ClientTelemetryEventCollector;\n private configSet: ConfigSet | undefined;\n private handlersMap: Map<string, WatchHandlerWithOptions<any>[]> = new Map();\n private transport: Transport;\n private eventEmitter = new Emitter<ClientEvents>();\n private timeout: number;\n private ready = false;\n private readyPromise: Promise<void> | undefined;\n private readyResolve: (() => void) | undefined;\n private currentContext?: ConfigDirectorContext;\n private streaming: boolean;\n\n constructor(clientSdkKey: string, sdkOptions: IdentifyingSdkOptions, clientOptions?: ConfigDirectorClientOptions) {\n this.logger = clientOptions?.logger ?? createDefaultLogger();\n this.timeout = clientOptions?.connection?.timeout ?? 3_000;\n const baseUrl = this.parseUrl(clientOptions?.connection?.url) ?? defaultBaseUrl;\n this.streaming = clientOptions?.connection?.streaming === false ? false : true;\n const transportConstructor = this.streaming ? StreamingTransport : PullTransport;\n this.usageEventCollector = new ClientTelemetryEventCollector({\n sdkKey: clientSdkKey,\n logger: this.logger,\n baseUrl,\n });\n this.transport = new transportConstructor({\n clientSdkKey,\n baseUrl,\n metaContext: {\n ...clientOptions?.metadata,\n sdkName: sdkOptions.sdkName,\n sdkVersion: sdkOptions.sdkVersion,\n userAgent: navigator?.userAgent,\n },\n logger: this.logger,\n });\n\n this.transport.on(\"configSetReceived\", (configSet: ConfigSet) => {\n this.readyResolve?.();\n const configKeys = Object.keys(configSet.configs);\n if (!this.configSet || configSet.kind == \"full\") {\n this.configSet = configSet;\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configSet.configs);\n } else {\n this.configSet.configs = {\n ...this.configSet.configs,\n ...configSet.configs,\n };\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configSet.configs);\n }\n this.logger.debug(\"[ConfigDirectorClient] ConfigSet updated from server:\", { keys: configKeys });\n });\n }\n\n public async initialize(context?: ConfigDirectorContext) {\n await this.connectToTransport(context, \"initialization\");\n }\n\n public async updateContext(context: ConfigDirectorContext) {\n await this.connectToTransport(context, \"context update\");\n }\n\n private async connectToTransport(context: ConfigDirectorContext | undefined, caller: ClientConnectAction) {\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\", { action: caller });\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(context ?? {}, this.timeout);\n this.currentContext = context;\n this.usageEventCollector.updateContext(context);\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 ${caller} after ${this.timeout}ms. ${warningDetails}`,\n );\n }\n } catch (error) {\n this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);\n }\n }\n\n private updateWatchers(configsMap: ConfigStateMap) {\n Object.values(configsMap).forEach((v) => this.updateWatchersForConfig(v));\n }\n\n private updateWatchersForConfig(configState: ConfigState) {\n this.handlersMap.get(configState.key)?.forEach((h) => {\n const value = this.getValueFromConfigState(configState.key, configState, h.defaultValue);\n h.handler(value);\n });\n }\n\n public watch<T extends ConfigValueType>(configKey: string, defaultValue: T, callback: WatchHandler<T>) {\n this.validateDefaultValue(defaultValue);\n\n const handlers = this.handlersMap.get(configKey);\n const handlerWithOptions = { handler: callback, defaultValue, requestedType: typeof defaultValue };\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>(configKey: string, defaultValue: T): T {\n this.validateDefaultValue(defaultValue);\n\n const configState = this.configSet?.configs[configKey];\n return this.getValueFromConfigState(configKey, configState, defaultValue);\n }\n\n private getValueFromConfigState<T extends ConfigValueType>(\n configKey: string,\n configState: ConfigState | undefined,\n defaultValue: T,\n ): T {\n if (!configState) {\n this.logger.debug(\n `[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`,\n );\n this.usageEventCollector.evaluatedConfig({\n contextId: this.currentContext?.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 const parseResult = parseConfigValue<T>(configState, defaultValue);\n this.usageEventCollector.evaluatedConfig({\n contextId: this.currentContext?.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 }\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: (event: ClientEvents[T]) => void): void {\n this.eventEmitter.on(eventName, handler);\n }\n\n public off<T extends keyof ClientEvents>(eventName: T, handler?: (payload: ClientEvents[T]) => void) {\n this.eventEmitter.off(eventName, handler);\n }\n\n public clear() {\n this.logger.debug(\"[ConfigDirectorClient] clear() has been called, removing all observers\");\n this.eventEmitter.clear();\n this.handlersMap.clear();\n }\n\n public unwatchAll() {\n this.handlersMap.clear();\n }\n\n public close() {\n this.logger.debug(\"[ConfigDirectorClient] close() has been called, closing connection to server\");\n this.transport.close();\n this.ready = false;\n }\n\n public dispose() {\n this.clear();\n this.close();\n }\n}\n","import {\n ConfigDirectorClient,\n ConfigDirectorClientOptions,\n ConfigDirectorLoggingLevel,\n ConfigDirectorLogMessageDecorator,\n createDefaultLogger,\n DefaultConfigDirectorClient,\n} from \"@js-client-core/index\";\n\n/**\n * Creates a `ConfigDirectorClient` object with the given `clientSdkKey` and optional\n * `clientOptions`. The returned client needs to be initialized before it is ready to serve\n * config values.\n *\n * @param clientSdkKey The client SDK key obtained from the ConfigDirector dashboard\n * @param clientOptions {@link ConfigDirectorClientOptions} options for the client (optional)\n * @returns A {@link ConfigDirectorClient} object\n *\n * @example\n * import { createClient } from \"@configdirector/client-sdk\";\n * const client = createClient(\"YOUR-SDK-KEY\");\n * await client.initialize();\n */\nexport const createClient = (\n clientSdkKey: string,\n clientOptions?: ConfigDirectorClientOptions,\n): ConfigDirectorClient => {\n return new DefaultConfigDirectorClient(\n clientSdkKey,\n { sdkName: \"js-client-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-client.mjs","names":["this","this","this","this","this","this","this"],"sources":["../../js-client-core/src/Emitter.ts","../../shared/src/errors.ts","../../js-client-core/src/errors.ts","../../eventsource/src/errors.ts","../../eventsource/src/EventSourceParser.ts","../../eventsource/src/types.ts","../../eventsource/src/EventSourceClient.ts","../../eventsource/src/index.ts","../../js-client-core/src/StreamingTransport.ts","../../shared/src/value-parser.ts","../../shared/src/logger.ts","../../js-client-core/src/logger.ts","../../shared/src/telemetry/utils.ts","../../js-client-core/src/telemetry/ClientEventReporter.ts","../../shared/src/telemetry/EventAggregator.ts","../../shared/src/telemetry/EventQueue.ts","../../shared/src/telemetry/TelemetryEventCollector.ts","../../js-client-core/src/telemetry/ClientTelemetryEventCollector.ts","../../shared/src/fetchWithTimeout.ts","../../js-client-core/src/PullTransport.ts","../../js-client-core/src/DefaultConfigDirectorClient.ts","../src/api.ts"],"sourcesContent":["type EventType = string | symbol;\ntype EventsRecord = Record<EventType, any>;\n\ntype Handler<TEventsRecord extends EventsRecord, TKey extends keyof TEventsRecord> = (\n payload: TEventsRecord[TKey],\n) => void;\n\nexport interface EventProvider<TEventsRecord extends EventsRecord> {\n on<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>): void;\n off<TName extends keyof TEventsRecord>(name: TName, handler?: Handler<TEventsRecord, TName>): void;\n clear(): void;\n}\n\nexport class Emitter<TEventsRecord extends EventsRecord> implements EventProvider<TEventsRecord> {\n private handlerMap: Map<keyof TEventsRecord, Array<(payload: any) => void>> = new Map();\n\n on<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>) {\n const handlers = this.handlerMap.get(name);\n if (handlers) {\n handlers.push(handler);\n } else {\n this.handlerMap.set(name, [handler]);\n }\n }\n\n once<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>) {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n function onceHandler(payload: TEventsRecord[keyof TEventsRecord]) {\n self.off(name, onceHandler);\n handler.apply(self, payload);\n }\n this.on(name, onceHandler);\n }\n\n off<TName extends keyof TEventsRecord>(name: TName, handler?: Handler<TEventsRecord, TName>) {\n const handlers = this.handlerMap.get(name);\n if (!handlers) {\n return;\n }\n\n if (handler) {\n const listenerIndex = handlers.indexOf(handler);\n if (listenerIndex >= 0) {\n handlers.splice(listenerIndex, 1);\n }\n } else {\n this.handlerMap.set(name, []);\n }\n }\n\n clear() {\n this.handlerMap.clear();\n }\n\n emit<TName extends keyof TEventsRecord>(name: TName, payload: TEventsRecord[TName]) {\n const handlers = this.handlerMap.get(name);\n if (!handlers) {\n return;\n }\n\n handlers.slice().map((h) => h(payload));\n }\n}\n","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","export { ConfigDirectorConnectionError, ConfigDirectorValidationError } from \"../../shared/src/errors\";\n\nexport const isFetchErrorFatal = (fetchError: any): boolean => {\n if (fetchError instanceof DOMException) {\n const domError = fetchError as DOMException;\n if (domError.name === \"NotAllowedError\") {\n return true;\n }\n } else if (fetchError instanceof TypeError) {\n return true;\n }\n\n return false;\n};\n","export class StreamClosedError extends Error {\n public override readonly name: string = \"StreamClosedError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, StreamClosedError.prototype);\n }\n}\n\nexport class MissingResponseBodyError extends Error {\n public override readonly name: string = \"MissingResponseBodyError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, MissingResponseBodyError.prototype);\n }\n}\n\nexport class InvalidOptionError extends Error {\n public override readonly name: string = \"InvalidOptionError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, InvalidOptionError.prototype);\n }\n}\n\nexport class ValueOutOfRangeError extends Error {\n public override readonly name: string = \"ValueOutOfRangeError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ValueOutOfRangeError.prototype);\n }\n}\n","import {\n EventSourceParserOptions,\n EventSourceMessageHandler,\n EventSourceCommentHandler,\n EventParserRetryCallback,\n} from \"./types\";\n\ntype LineContent = {\n field: string;\n value: string;\n};\n\nexport class EventSourceParser {\n private isFirstChunk = true;\n private previousIncompleteLine = \"\";\n private currentEvent: { type?: string | undefined; data: string };\n private lastEventId: string | undefined;\n private onEvent: EventSourceMessageHandler;\n private onRetry: EventParserRetryCallback | undefined;\n private onComment: EventSourceCommentHandler | undefined;\n\n constructor(options: EventSourceParserOptions) {\n this.currentEvent = {\n data: \"\",\n };\n this.onEvent = options.onEvent ?? (() => {});\n this.onRetry = options.onRetry;\n this.onComment = options.onComment;\n }\n\n public parse(input: string) {\n const chunk = this.prepareChunk(input);\n const lines = this.splitLines(chunk);\n for (const line of lines) {\n this.parseAndProcessLine(line);\n }\n }\n\n public finish() {\n if (this.previousIncompleteLine !== \"\") {\n this.parseAndProcessLine(this.previousIncompleteLine);\n }\n }\n\n private parseAndProcessLine(line: string) {\n if (line.startsWith(\":\")) {\n this.onComment?.(this.getValueFromLine(0, line));\n return;\n }\n\n if (line === \"\") {\n this.publishEvent();\n return;\n }\n\n const content = this.parseContentLine(line);\n this.processContent(content);\n }\n\n private publishEvent() {\n const data = this.currentEvent.data.endsWith(\"\\n\")\n ? this.currentEvent.data.slice(0, -1)\n : this.currentEvent.data;\n\n if (data) {\n this.onEvent({\n ...this.currentEvent,\n id: this.lastEventId,\n data,\n });\n }\n this.currentEvent = {\n data: \"\",\n };\n }\n\n private processContent(content: LineContent) {\n switch (content.field) {\n case \"event\":\n this.currentEvent.type = content.value;\n break;\n case \"data\":\n this.currentEvent.data += content.value + \"\\n\";\n break;\n case \"id\":\n if (!content.value.includes(\"\\0\")) {\n this.lastEventId = content.value;\n }\n break;\n case \"retry\":\n if (/^\\d+$/.test(content.value)) {\n this.onRetry?.(Number.parseInt(content.value, 10));\n }\n break;\n\n default:\n break;\n }\n }\n\n private parseContentLine(line: string): LineContent {\n const delimiterIndex = line.indexOf(\":\");\n if (delimiterIndex === -1) {\n return { field: line, value: \"\" };\n }\n\n return { field: line.slice(0, delimiterIndex), value: this.getValueFromLine(delimiterIndex, line) };\n }\n\n private splitLines(chunk: string) {\n const lines = chunk.split(/\\r\\n|\\r|\\n/);\n this.previousIncompleteLine = lines.pop() ?? \"\";\n return lines;\n }\n\n private getValueFromLine(delimiterIndex: number, line: string): string {\n if (delimiterIndex === -1) {\n return \"\";\n }\n\n const valueStartIndex = line[delimiterIndex + 1] === \" \" ? delimiterIndex + 2 : delimiterIndex + 1;\n return line.slice(valueStartIndex);\n }\n\n private prepareChunk(chunk: string) {\n if (this.isFirstChunk) {\n this.isFirstChunk = false;\n return this.previousIncompleteLine + chunk.replace(/^\\xEF\\xBB\\xBF/, \"\").replace(/^\\uFEFF/, \"\");\n } else {\n return this.previousIncompleteLine + chunk;\n }\n }\n}\n","export type ReconnectionState = {\n attempt: number;\n serverReconnectionTime: number;\n status?: number | undefined;\n error?: Error | undefined;\n};\n\nexport enum ReadyState {\n OPEN = \"open\",\n CLOSED = \"closed\",\n CONNECTING = \"connecting\",\n}\n\nexport type EventSourceClientOptions = {\n url: URL | string;\n method?: string;\n headers?: Record<string, string>;\n body?: any;\n mode?: \"cors\" | \"no-cors\" | \"same-origin\";\n credentials?: \"include\" | \"omit\" | \"same-origin\";\n redirect?: \"error\" | \"follow\";\n referrer?: string;\n referrerPolicy?: ReferrerPolicy;\n lastEventId?: string;\n onError?: (error: Error) => void;\n onConnect?: () => void;\n onDisconnect?: () => void;\n onMessage?: EventSourceMessageHandler;\n onComment?: EventSourceCommentHandler;\n shouldReconnect?: (state: ReconnectionState) => boolean;\n calculateReconnectDelay?: (state: ReconnectionState) => number;\n};\n\nexport type EventSourceMessage = {\n id?: string | undefined;\n type?: string;\n data: string;\n};\n\nexport type EventSourceMessageHandler = (message: EventSourceMessage) => void;\nexport type EventSourceCommentHandler = (comment: string) => void;\n\nexport type EventParserRetryCallback = (retry: number) => void;\n\nexport type EventSourceParserOptions = {\n onEvent?: EventSourceMessageHandler;\n onRetry?: EventParserRetryCallback;\n onComment?: EventSourceCommentHandler;\n};\n","import {\n InvalidOptionError,\n MissingResponseBodyError,\n StreamClosedError,\n ValueOutOfRangeError,\n} from \"./errors\";\nimport { EventSourceParser } from \"./EventSourceParser\";\nimport {\n EventSourceClientOptions,\n EventSourceCommentHandler,\n EventSourceMessageHandler,\n ReconnectionState,\n ReadyState,\n} from \"./types\";\n\nexport class EventSourceClient {\n private readonly url: URL;\n private readonly headers?: Record<string, string>;\n private readonly requestOptions;\n private lastEventId?: string;\n private reconnectAttempt: number = 0;\n private abortController: AbortController = new AbortController();\n private reconnectTimeout: ReturnType<typeof setTimeout> | undefined;\n private _readyState: ReadyState = ReadyState.CLOSED;\n private serverReconnectionTime: number = 2_000;\n private _onError: (error: Error) => void;\n private _onMessage: EventSourceMessageHandler;\n private _onComment: EventSourceCommentHandler;\n private _onConnect: () => void;\n private _onDisconnect: () => void;\n private _shouldReconnect: (state: ReconnectionState) => boolean;\n private _calculateReconnectDelay: (state: ReconnectionState) => number;\n\n constructor(options: EventSourceClientOptions) {\n this.url = new URL(options.url);\n this.lastEventId = options.lastEventId;\n this.headers = {\n Accept: \"text/event-stream\",\n ...options.headers,\n };\n this.requestOptions = {\n body: options.body,\n method: options.method ?? \"GET\",\n mode: options.mode,\n credentials: options.credentials,\n redirect: options.redirect,\n referrer: options.referrer,\n referrerPolicy: options.referrerPolicy,\n };\n this._onError = this.getValidatedFunctionOrNoOp(options.onError, \"onError\");\n this._onConnect = this.getValidatedFunctionOrNoOp(options.onConnect, \"onConnect\");\n this._onDisconnect = this.getValidatedFunctionOrNoOp(options.onDisconnect, \"onDisconnect\");\n this._onMessage = this.getValidatedFunctionOrNoOp(options.onMessage, \"onMessage\");\n this._onComment = this.getValidatedFunctionOrNoOp(options.onComment, \"onComment\");\n this._shouldReconnect = this.getValidatedFunctionOr(\n options.shouldReconnect,\n () => true,\n \"shouldReconnect\",\n );\n this._calculateReconnectDelay = this.getValidatedFunctionOr(\n options.calculateReconnectDelay,\n () => this.serverReconnectionTime,\n \"calculateReconnectDelay\",\n );\n }\n\n public set onMessage(value: EventSourceMessageHandler) {\n this._onMessage = this.getValidatedFunctionOrNoOp(value, \"onMessage\");\n }\n\n public set onComment(value: EventSourceCommentHandler) {\n this._onComment = this.getValidatedFunctionOrNoOp(value, \"onComment\");\n }\n\n public set onConnect(value: () => void) {\n this._onConnect = this.getValidatedFunctionOrNoOp(value, \"onConnect\");\n }\n\n public set onDisconnect(value: () => void) {\n this._onDisconnect = this.getValidatedFunctionOrNoOp(value, \"onDisconnect\");\n }\n\n public get readyState(): ReadyState {\n return this._readyState;\n }\n\n public connect() {\n if (this._readyState !== ReadyState.CLOSED) {\n return;\n }\n\n this.reconnectAttempt = 0;\n this.initiateConnection();\n }\n\n private initiateConnection() {\n this.abortController = new AbortController();\n this._readyState = ReadyState.CONNECTING;\n fetch(this.url, {\n ...this.requestOptions,\n headers: this.buildHeaders(),\n signal: this.abortController.signal,\n cache: \"no-store\",\n })\n .then(this.handleFetchResponse.bind(this))\n .catch((error: Error) => {\n if (error.name === \"AbortError\" || this.abortController.signal.aborted) {\n this.close();\n return;\n }\n\n this._onError(error);\n this.scheduleReconnect({ error });\n });\n }\n\n public close() {\n clearTimeout(this.reconnectTimeout);\n this._readyState = ReadyState.CLOSED;\n this.abortController.abort();\n }\n\n private disconnected() {\n clearTimeout(this.reconnectTimeout);\n this._readyState = ReadyState.CLOSED;\n this._onDisconnect();\n }\n\n private buildHeaders(): Record<string, string> {\n const lastEventIdHeader = this.lastEventId ? { \"Last-Event-ID\": this.lastEventId } : undefined;\n return {\n ...this.headers,\n ...lastEventIdHeader,\n };\n }\n\n private async handleFetchResponse(response: Response) {\n const { body, status } = response;\n\n if (status === 204) {\n this.disconnected();\n return;\n }\n\n if (status >= 400) {\n this.scheduleReconnect({ status });\n return;\n }\n\n if (!body) {\n throw new MissingResponseBodyError(\"The server response did not have a body\");\n }\n\n this._readyState = ReadyState.OPEN;\n this._onConnect();\n this.reconnectAttempt = 0;\n\n const stream = body?.pipeThrough(new TextDecoderStream());\n const reader = stream.getReader();\n const parser = new EventSourceParser({\n onEvent: (event) => {\n if (typeof event.id === \"string\") {\n this.lastEventId = event.id;\n }\n this._onMessage(event);\n },\n onComment: (comment) => this._onComment(comment),\n onRetry: (retryDelay) => (this.serverReconnectionTime = retryDelay),\n });\n\n let done = false;\n while (!done) {\n const { done: readerDone, value } = await reader.read();\n if (value) {\n parser.parse(value);\n }\n\n done = readerDone;\n }\n\n parser.finish();\n this.scheduleReconnect({ status, error: new StreamClosedError(\"The server response stream was closed\") });\n }\n\n private scheduleReconnect(state: { status?: number; error?: Error }) {\n if (this.abortController.signal.aborted) {\n return;\n }\n\n clearTimeout(this.reconnectTimeout);\n this.reconnectAttempt += 1;\n const reconnectionState = {\n attempt: this.reconnectAttempt,\n status: state.status,\n error: state.error,\n serverReconnectionTime: this.serverReconnectionTime,\n };\n if (this._shouldReconnect(reconnectionState)) {\n this._readyState = ReadyState.CONNECTING;\n let delay = this._calculateReconnectDelay(reconnectionState);\n if (!Number.isFinite(delay) || delay < 1 || delay > 1_000 * 60 * 60) {\n this._onError(\n new ValueOutOfRangeError(\n `The calculated reconnect delay is out of range: ${delay}. Defaulting to ${this.serverReconnectionTime}`,\n ),\n );\n delay = this.serverReconnectionTime;\n }\n this.reconnectTimeout = setTimeout(() => this.initiateConnection(), delay);\n } else {\n this.disconnected();\n return;\n }\n }\n\n private getValidatedFunctionOrNoOp(func: any, name: string): (...args: any) => any {\n return this.getValidatedFunctionOr(func, () => {}, name);\n }\n\n private getValidatedFunctionOr(\n func: any,\n defaultFunc: (...args: any) => any,\n name: string,\n ): (...args: any) => any {\n if (func == null) {\n return defaultFunc;\n } else if (typeof func === \"function\") {\n return func;\n } else {\n throw new InvalidOptionError(`${name} must be a function`);\n }\n }\n}\n","import { EventSourceClient } from \"./EventSourceClient\";\nimport { EventSourceClientOptions } from \"./types\";\n\nexport const createEventSourceClient = (options: EventSourceClientOptions) => {\n return new EventSourceClient(options);\n};\n","import type {\n ConfigDirectorContext,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { Emitter } from \"./Emitter\";\nimport { ConfigDirectorConnectionError } from \"./errors\";\nimport { createEventSourceClient } from \"@eventsource/index\";\nimport { EventSourceClient } from \"@eventsource/EventSourceClient\";\n\nexport class StreamingTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventSource: EventSourceClient | undefined;\n private eventEmitter = new Emitter<TransportEvents>();\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(\"client/sse/v1\", options.baseUrl);\n }\n\n public async connect(context: ConfigDirectorContext, timeout: number): Promise<this> {\n if (this.eventSource) {\n this.close();\n }\n\n const eventSourcePromise = new Promise<this>((resolve, reject) => {\n this.eventSource = createEventSourceClient({\n url: this.url,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n givenContext: context,\n metaContext: this.options.metaContext,\n clientSdkKey: this.options.clientSdkKey,\n }),\n\n onMessage: ({ data }) => {\n this.dispatchMessage(data);\n },\n onConnect: () => {\n this.logger.debug(\"[EventSourceTransport] Connected\");\n resolve(this);\n },\n shouldReconnect: (state) => {\n const reconnect = !this.isStatusFatal(state.status);\n if (!reconnect) {\n reject(this.prepareFatalError(state.status, state.error));\n }\n return reconnect;\n },\n calculateReconnectDelay: (state) => {\n const delay = 2_000 * state.attempt;\n this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${delay}ms.`);\n return delay;\n },\n onDisconnect: () => {\n this.logger.debug(\"[EventSourceTransport] Disconnected\");\n },\n });\n this.eventSource.connect();\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(\"configSetReceived\", json);\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 | undefined,\n error: Error | undefined,\n ): ConfigDirectorConnectionError {\n const status = responseStatus ?? 0;\n const headline = `Connection failed with status: ${responseStatus ?? \"unknown\"}.`;\n const errorLine = error ? ` Error: ${error}` : \"\";\n const message = `${headline}${errorLine}. 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 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 this.eventEmitter.off(name, handler);\n }\n\n public clear(): void {\n this.eventEmitter.clear();\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 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 { ConfigDirectorLogMessageDecorator, ConfigDirectorLoggingLevel } from \"./types\";\nimport { DefaultConsoleLogger } from \"../../shared/src/logger\";\nexport { DefaultConsoleLogger } from \"../../shared/src/logger\";\n\nclass LogMessageDecorator implements ConfigDirectorLogMessageDecorator {\n decorateMessage(message: string): string {\n return `[ConfigDirector:js-client-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 { isDroppedEventsEmpty, isEventListEmpty } from \"@shared/telemetry/utils\";\nimport { ConfigDirectorContext, ConfigDirectorLogger } from \"../types\";\nimport {\n AggregatedEventList,\n DiscreteEventList,\n DroppedEvents,\n EventReport,\n EventReporter,\n ReporterResponse,\n} from \"./types\";\n\nexport type EventReporterOptions = {\n sdkKey: string;\n logger: ConfigDirectorLogger;\n baseUrl: URL;\n};\n\nexport class ClientEventReporter 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(\"client/telemetry/v1\", options.baseUrl);\n }\n\n public async report({\n context,\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n }: {\n context?: ConfigDirectorContext | undefined;\n discreteEvents: DiscreteEventList;\n aggregatedEvents: AggregatedEventList;\n droppedEvents?: DroppedEvents;\n }): Promise<ReporterResponse> {\n if (!this.executeRequests) {\n return { success: false, fatalError: true };\n }\n\n const eventReport: EventReport = {\n clientSdkKey: this.sdkKey,\n context,\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n };\n if (this.isReportEmpty(eventReport)) {\n return { success: true, fatalError: false };\n }\n\n const response = 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 sendReport(eventReport: EventReport): ReporterResponse {\n try {\n const result = navigator.sendBeacon(this.url, JSON.stringify(eventReport));\n return { success: result, fatalError: false };\n } catch (beaconError) {\n this.logger.warn(\n `[EventReporter] Fatal error attempting to send telemetry data: ${beaconError}. No more telemetry data will be sent.`,\n );\n return { success: false, fatalError: true };\n }\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","import { ConfigDirectorContext, ConfigValueType } from \"../types\";\nimport { ClientEventReporter } from \"./ClientEventReporter\";\nimport { EvaluatedConfigEvent } from \"./telemetry-events\";\nimport { EventReporter } from \"./types\";\nimport { TelemetryEventCollector, TelemetryEventCollectorOptions } from \"@shared/telemetry/TelemetryEventCollector\";\n\nexport class ClientTelemetryEventCollector 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 ClientEventReporter(options);\n try {\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.flush();\n }\n });\n } catch (error) {\n this.logger.warn(\"[TelemetryEventCollector] Could not configure 'visibilitychange' listener: \", error);\n }\n }\n\n public async updateContext(value: ConfigDirectorContext | undefined) {\n this.cancelScheduledFlush();\n await this.flush();\n this._context = value;\n await this.flushAndScheduleNext();\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 ...event,\n defaultValue: this.sanitizeValue(event.defaultValue, event.type),\n evaluatedValue: this.sanitizeValue(event.evaluatedValue, event.type),\n };\n }\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 type {\n ConfigDirectorContext,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { Emitter } from \"./Emitter\";\nimport { ConfigDirectorConnectionError, isFetchErrorFatal } from \"./errors\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\n\nexport class PullTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventEmitter = new Emitter<TransportEvents>();\n private url: URL;\n private fatalError = false;\n\n constructor(private readonly options: TransportOptions) {\n this.options = options;\n this.logger = options.logger;\n this.url = new URL(\"client/pull/v1\", options.baseUrl);\n }\n\n public async connect(context: ConfigDirectorContext, timeout: number): Promise<this> {\n if (this.fatalError) {\n this.logger.warn(\n \"[PullTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.\",\n );\n return this;\n }\n\n try {\n const response = await fetchWithTimeout(timeout, this.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n givenContext: context,\n metaContext: this.options.metaContext,\n clientSdkKey: this.options.clientSdkKey,\n }),\n }, this.logger);\n\n if (!response.ok) {\n if (this.isStatusFatal(response.status)) {\n this.fatalError = true;\n throw this.prepareFatalResponseStatusError(response.status, await response.text());\n } else {\n throw new ConfigDirectorConnectionError(\n `Connection failed with status: ${response.status}`,\n response.status,\n );\n }\n }\n\n const json = JSON.parse(await response.text());\n this.eventEmitter.emit(\"configSetReceived\", json);\n return this;\n } catch (fetchError) {\n if (isFetchErrorFatal(fetchError)) {\n this.fatalError = true;\n throw new ConfigDirectorConnectionError(\n `Connection failed with fatal error: ${fetchError}. This is an unrecoverable error, retry attempts will be ignored.`,\n );\n } else if (fetchError instanceof SyntaxError) {\n throw new ConfigDirectorConnectionError(\n `Failed to parse the response from the server: ${fetchError}`,\n );\n } else {\n throw new ConfigDirectorConnectionError(`Connection failed with error: ${fetchError}.`);\n }\n }\n }\n\n private prepareFatalResponseStatusError(\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, retry attempts will be ignored.`;\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 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 this.eventEmitter.off(name, handler);\n }\n\n public clear(): void {\n this.eventEmitter.clear();\n }\n\n public close() {}\n\n public dispose(): void {\n this.close();\n this.clear();\n }\n}\n","import { Emitter } from \"./Emitter\";\nimport { StreamingTransport } from \"./StreamingTransport\";\nimport { getRequestedType, parseConfigValue } from \"../../shared/src/value-parser\";\nimport {\n ConfigSet,\n ConfigState,\n ConfigStateMap,\n ConfigDirectorContext,\n ConfigDirectorClientOptions,\n ConfigDirectorClient,\n ClientEvents,\n WatchHandler,\n ConfigValueType,\n ConfigDirectorLogger,\n Transport,\n ClientConnectAction,\n IdentifyingSdkOptions,\n} from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\nimport { ClientTelemetryEventCollector } from \"./telemetry\";\nimport { ConfigDirectorValidationError } from \"./errors\";\nimport { PullTransport } from \"./PullTransport\";\n\nconst defaultBaseUrl = new URL(\"https://client-sdk-api.configdirector.com\");\n\ntype WatchHandlerWithOptions<T extends ConfigValueType> = {\n handler: WatchHandler<T>;\n defaultValue: T;\n requestedType: string;\n};\n\nexport class DefaultConfigDirectorClient implements ConfigDirectorClient {\n private logger: ConfigDirectorLogger;\n private usageEventCollector: ClientTelemetryEventCollector;\n private configSet: ConfigSet | undefined;\n private handlersMap: Map<string, WatchHandlerWithOptions<any>[]> = new Map();\n private transport: Transport;\n private eventEmitter = new Emitter<ClientEvents>();\n private timeout: number;\n private ready = false;\n private readyPromise: Promise<void> | undefined;\n private readyResolve: (() => void) | undefined;\n private currentContext?: ConfigDirectorContext;\n private streaming: boolean;\n\n constructor(clientSdkKey: string, sdkOptions: IdentifyingSdkOptions, clientOptions?: ConfigDirectorClientOptions) {\n this.logger = clientOptions?.logger ?? createDefaultLogger();\n this.timeout = clientOptions?.connection?.timeout ?? 3_000;\n const baseUrl = this.parseUrl(clientOptions?.connection?.url) ?? defaultBaseUrl;\n this.streaming = clientOptions?.connection?.streaming === false ? false : true;\n const transportConstructor = this.streaming ? StreamingTransport : PullTransport;\n this.usageEventCollector = new ClientTelemetryEventCollector({\n sdkKey: clientSdkKey,\n logger: this.logger,\n baseUrl,\n });\n this.transport = new transportConstructor({\n clientSdkKey,\n baseUrl,\n metaContext: {\n ...clientOptions?.metadata,\n sdkName: sdkOptions.sdkName,\n sdkVersion: sdkOptions.sdkVersion,\n userAgent: navigator?.userAgent,\n },\n logger: this.logger,\n });\n\n this.transport.on(\"configSetReceived\", (configSet: ConfigSet) => {\n this.readyResolve?.();\n const configKeys = Object.keys(configSet.configs);\n if (!this.configSet || configSet.kind == \"full\") {\n this.configSet = configSet;\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configSet.configs);\n } else {\n this.configSet.configs = {\n ...this.configSet.configs,\n ...configSet.configs,\n };\n this.eventEmitter.emit(\"configsUpdated\", { keys: configKeys });\n this.updateWatchers(configSet.configs);\n }\n this.logger.debug(\"[ConfigDirectorClient] ConfigSet updated from server:\", { keys: configKeys });\n });\n }\n\n public async initialize(context?: ConfigDirectorContext) {\n await this.connectToTransport(context, \"initialization\");\n }\n\n public async updateContext(context: ConfigDirectorContext) {\n await this.connectToTransport(context, \"context update\");\n }\n\n private async connectToTransport(context: ConfigDirectorContext | undefined, caller: ClientConnectAction) {\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\", { action: caller });\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(context ?? {}, this.timeout);\n this.currentContext = context;\n this.usageEventCollector.updateContext(context);\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 ${caller} after ${this.timeout}ms. ${warningDetails}`,\n );\n }\n } catch (error) {\n this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);\n }\n }\n\n private updateWatchers(configsMap: ConfigStateMap) {\n Object.values(configsMap).forEach((v) => this.updateWatchersForConfig(v));\n }\n\n private updateWatchersForConfig(configState: ConfigState) {\n this.handlersMap.get(configState.key)?.forEach((h) => {\n const value = this.getValueFromConfigState(configState.key, configState, h.defaultValue);\n h.handler(value);\n });\n }\n\n public watch<T extends ConfigValueType>(configKey: string, defaultValue: T, callback: WatchHandler<T>) {\n this.validateDefaultValue(defaultValue);\n\n const handlers = this.handlersMap.get(configKey);\n const handlerWithOptions = { handler: callback, defaultValue, requestedType: typeof defaultValue };\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>(configKey: string, defaultValue: T): T {\n this.validateDefaultValue(defaultValue);\n\n const configState = this.configSet?.configs[configKey];\n return this.getValueFromConfigState(configKey, configState, defaultValue);\n }\n\n private getValueFromConfigState<T extends ConfigValueType>(\n configKey: string,\n configState: ConfigState | undefined,\n defaultValue: T,\n ): T {\n if (!configState) {\n this.logger.debug(\n `[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`,\n );\n this.usageEventCollector.evaluatedConfig({\n contextId: this.currentContext?.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 const parseResult = parseConfigValue<T>(configState, defaultValue);\n this.usageEventCollector.evaluatedConfig({\n contextId: this.currentContext?.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 }\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: (event: ClientEvents[T]) => void): void {\n this.eventEmitter.on(eventName, handler);\n }\n\n public off<T extends keyof ClientEvents>(eventName: T, handler?: (payload: ClientEvents[T]) => void) {\n this.eventEmitter.off(eventName, handler);\n }\n\n public clear() {\n this.logger.debug(\"[ConfigDirectorClient] clear() has been called, removing all observers\");\n this.eventEmitter.clear();\n this.handlersMap.clear();\n }\n\n public unwatchAll() {\n this.handlersMap.clear();\n }\n\n public close() {\n this.logger.debug(\"[ConfigDirectorClient] close() has been called, closing connection to server\");\n this.transport.close();\n this.ready = false;\n }\n\n public dispose() {\n this.clear();\n this.close();\n }\n}\n","import {\n ConfigDirectorClient,\n ConfigDirectorClientOptions,\n ConfigDirectorLoggingLevel,\n ConfigDirectorLogMessageDecorator,\n createDefaultLogger,\n DefaultConfigDirectorClient,\n} from \"@js-client-core/index\";\n\n/**\n * Creates a `ConfigDirectorClient` object with the given `clientSdkKey` and optional\n * `clientOptions`. The returned client needs to be initialized before it is ready to serve\n * config values.\n *\n * @param clientSdkKey The client SDK key obtained from the ConfigDirector dashboard\n * @param clientOptions {@link ConfigDirectorClientOptions} options for the client (optional)\n * @returns A {@link ConfigDirectorClient} object\n *\n * @example\n * import { createClient } from \"@configdirector/client-sdk\";\n * const client = createClient(\"YOUR-SDK-KEY\");\n * await client.initialize();\n */\nexport const createClient = (\n clientSdkKey: string,\n clientOptions?: ConfigDirectorClientOptions,\n): ConfigDirectorClient => {\n return new DefaultConfigDirectorClient(\n clientSdkKey,\n { sdkName: \"js-client-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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configdirector/client-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Javascript client SDK for ConfigDirector. ConfigDirector is a remote configuration and feature flag service.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ConfigDirector",
|
|
@@ -46,9 +46,6 @@
|
|
|
46
46
|
"coverage": "vitest run --coverage",
|
|
47
47
|
"ci:test": "vitest run"
|
|
48
48
|
},
|
|
49
|
-
"dependencies": {
|
|
50
|
-
"eventsource-client": "^1.2.0"
|
|
51
|
-
},
|
|
52
49
|
"devDependencies": {
|
|
53
50
|
"@eslint/js": "^10.0.1",
|
|
54
51
|
"@rollup/plugin-replace": "^6.0.3",
|
|
@@ -60,12 +57,11 @@
|
|
|
60
57
|
"msw": "^2.12.9",
|
|
61
58
|
"prettier": "^3.8.1",
|
|
62
59
|
"rimraf": "^6.1.3",
|
|
63
|
-
"tsdown": "^0.21.
|
|
60
|
+
"tsdown": "^0.21.7",
|
|
64
61
|
"tslib": "^2.8.1",
|
|
65
62
|
"typescript": "^5.9.3",
|
|
66
63
|
"typescript-eslint": "^8.58.0",
|
|
67
64
|
"vite": "^8.0.3",
|
|
68
|
-
"vite-tsconfig-paths": "^6.1.1",
|
|
69
65
|
"vitest": "^4.1.2"
|
|
70
66
|
}
|
|
71
67
|
}
|