@configdirector/nextjs-sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,4 +3,4 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.
3
3
  `?(t.push(e.slice(n,r)),i===`\r`&&e[r+1]===`
4
4
  `&&r++,r++,n=r):r++}return this.bufferedInput=e.slice(n),t}dispatchLine(e){if(e.startsWith(`:`)){this.onComment?.(this.extractValue(e,1));return}if(e===``){this.emitEvent();return}this.applyField(this.parseField(e))}emitEvent(){let e=this.currentEvent.data.endsWith(`
5
5
  `)?this.currentEvent.data.slice(0,-1):this.currentEvent.data;e!==``&&this.onEvent({...this.currentEvent,id:this.lastEventId,data:e}),this.currentEvent={data:``}}applyField(e){switch(e.field){case`event`:this.currentEvent.type=e.value;break;case`data`:this.currentEvent.data+=e.value+`
6
- `;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}}parseField(e){let t=e.indexOf(`:`);return t===-1?{field:e,value:``}:{field:e.slice(0,t),value:this.extractValue(e,t+1)}}extractValue(e,t){return e[t]===` `?e.slice(t+1):e.slice(t)}};let v=function(e){return e.OPEN=`open`,e.CLOSED=`closed`,e.CONNECTING=`connecting`,e}({});var y=class{constructor(e){this.DEFAULT_SHOULD_RECONNECT=()=>!0,this.DEFAULT_CALCULATE_RECONNECT_DELAY=()=>this.serverReconnectionTime,this.reconnectAttempt=0,this.abortController=new AbortController,this._readyState=v.CLOSED,this.serverReconnectionTime=2e3,this.url=e.url.toString(),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._fetch=e.fetch??((e,t)=>fetch(e,t)),this._onError=this.resolveCallback(e.onError,()=>{},`onError`),this._onConnect=this.resolveCallback(e.onConnect,()=>{},`onConnect`),this._onDisconnect=this.resolveCallback(e.onDisconnect,()=>{},`onDisconnect`),this._onMessage=this.resolveCallback(e.onMessage,()=>{},`onMessage`),this._onComment=this.resolveCallback(e.onComment,()=>{},`onComment`),this._shouldReconnect=this.resolveCallback(e.shouldReconnect,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`),this._calculateReconnectDelay=this.resolveCallback(e.calculateReconnectDelay,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`calculateReconnectDelay`)}set onError(e){this._onError=this.resolveCallback(e,()=>{},`onError`)}set onMessage(e){this._onMessage=this.resolveCallback(e,()=>{},`onMessage`)}set onComment(e){this._onComment=this.resolveCallback(e,()=>{},`onComment`)}set onConnect(e){this._onConnect=this.resolveCallback(e,()=>{},`onConnect`)}set onDisconnect(e){this._onDisconnect=this.resolveCallback(e,()=>{},`onDisconnect`)}set shouldReconnect(e){this._shouldReconnect=this.resolveCallback(e,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`)}set calculateReconnectDelay(e){this._calculateReconnectDelay=this.resolveCallback(e,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`shouldReconnect`)}get readyState(){return this._readyState}connect(){this._readyState===v.CLOSED&&(this.reconnectAttempt=0,this.initiateConnection())}close(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this.abortController.abort()}initiateConnection(){this.abortController=new AbortController,this._readyState=v.CONNECTING,this._fetch(this.url,{...this.requestOptions,headers:this.buildRequestHeaders(),signal:this.abortController.signal,cache:`no-store`}).then(e=>this.handleFetchResponse(e)).catch(e=>{if(e.name===`AbortError`||this.abortController.signal.aborted){this.close();return}this._onError(e),this.scheduleReconnect({error: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 m(`The server response did not have a body`);this._readyState=v.OPEN,this._onConnect(),this.reconnectAttempt=0;let r=new _({onEvent:e=>{typeof e.id==`string`&&(this.lastEventId=e.id),this._onMessage(e)},onComment:e=>this._onComment(e),onRetry:e=>this.serverReconnectionTime=e});await this.readResponseStream(t,r),this.scheduleReconnect({status:n,error:new p(`The server response stream was closed`)})}async readResponseStream(e,t){let n=e.getReader(),r=new TextDecoder,i=!1;for(;!i;){let e=await n.read();e.value&&t.parse(r.decode(e.value,{stream:!0})),i=e.done}t.finish()}disconnected(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this._onDisconnect()}buildRequestHeaders(){let e=this.lastEventId?{"Last-Event-ID":this.lastEventId}:void 0;return{...this.headers,...e}}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.disconnected();return}this._readyState=v.CONNECTING;let n=this._calculateReconnectDelay(t);(!Number.isFinite(n)||n<1||n>1e3*60*60)&&(this._onError(new g(`The calculated reconnect delay is out of range: ${n}. Defaulting to ${this.serverReconnectionTime}`)),n=this.serverReconnectionTime),this.reconnectTimeout=setTimeout(()=>this.initiateConnection(),n)}resolveCallback(e,t,n){if(e==null)return t;if(typeof e!=`function`)throw new h(`${n} must be a function`);return e}};const b=e=>new y(e);var x=class{constructor(e){this.options=e,this.eventEmitter=new u,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/sse/v1`,e.baseUrl)}async connect(e,t){this.eventSource&&this.close();let n=new Promise((t,n)=>{this.eventSource=b({url:this.url,method:`POST`,headers:{"Content-Type":`application/json`},fetch:this.options.fetch,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=this.options.connectionRetryDelay(e.attempt);return this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${t}ms.`),t},onError:e=>{this.logger.debug(`[EventSourceTransport] Error: `,e)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}}),this.eventSource.connect()});return clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,Promise.race([n,new Promise(e=>{this.timeoutTimer=setTimeout(()=>e(this),t)})]).finally(()=>clearTimeout(this.timeoutTimer))}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 n=e??0;return new d(`${`Connection failed with status: ${e??`unknown`}.`}${t?` Error: ${t}`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}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(),clearTimeout(this.timeoutTimer)}dispose(){this.close(),this.clear()}};const S=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},C=e=>e===`number`||e===`bigint`,w=(e,t)=>{let n=e.value,r=S(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=T(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(C(typeof t)&&e.type===`integer`){let e=E(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(C(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`}},T=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},E=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},O={off:-1,error:0,warn:1,info:2,debug:3},k=()=>{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 A=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=k()}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){O[this.level]>=O[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},j=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const M=(e,t)=>new A(e??`warn`,t??new j);var N=class{constructor(){this.fatalError=!1,this.pollingIntervalSeconds=60}clearPollingInterval(){clearInterval(this.pollingInterval)}schedulePollingInterval(e){clearInterval(this.pollingInterval),!(this.pollingIntervalSeconds<=0)&&(this.pollingInterval=setInterval(e,this.pollingIntervalSeconds*1e3))}async handleNonOkResponse(e){if(!e.ok)throw this.isStatusFatal(e.status)?(this.fatalError=!0,this.prepareFatalResponseStatusError(e.status,await e.text())):new d(`Connection failed with status: ${e.status}`,e.status)}handleFetchError(e){throw this.isFetchErrorFatal(e)?(this.fatalError=!0,this.close(),new d(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new d(`Failed to parse the response from the server: ${e}`):new d(`Connection failed with error: ${e}.`)}prepareFatalResponseStatusError(e,t){let n=e??0;return new d(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,n)}isStatusFatal(e){return!!e&&e>=400&&e<500}isFetchErrorFatal(e){return e?.name===`NotAllowedError`?!0:e instanceof TypeError}get isConnected(){return this.pollingInterval!==void 0}close(){clearInterval(this.pollingInterval),this.pollingInterval=void 0}dispose(){this.close(),this.clear()}};const P=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(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var F=class extends N{constructor(e){super(),this.options=e,this.eventEmitter=new u,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/polling/v1`,e.baseUrl),this.pollingIntervalSeconds=e.pollingInterval??60}async connect(e,t){return this.clearPollingInterval(),await this.fetchConfigs(e,t),this.schedulePollingInterval(()=>{this.fetchConfigs(e,t).catch(e=>{this.logger.error(`[PollingTransport] Error during polling:`,e)})}),this}async fetchConfigs(e,t){if(this.fatalError){this.logger.warn(`[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`);return}try{let n=await P(t,this.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:this.options.metaContext,clientSdkKey:this.options.clientSdkKey,lastUpdateTimestamp:this.lastUpdateTimestamp})},this.logger);if(await this.handleNonOkResponse(n),n.status==200){let e=JSON.parse(await n.text());this.lastUpdateTimestamp=e.timestamp,this.eventEmitter.emit(`configSetReceived`,e)}}catch(e){this.handleFetchError(e)}}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}},I=class extends F{constructor(e){super(e),this.pollingIntervalSeconds=0}};const L=(e,t)=>new URL(e,t?.toString()),R=e=>{if(e)try{return new URL(e)}catch(t){throw new f(`Invalid URL '${e}'. Parsing failed: ${t}`)}};var z=class{constructor(e,t,n,r,i){this.handlersMap=new Map,this.eventEmitter=new u,this.ready=!1,this.initializing=!1,this.logger=r?.logger??M(),this.timeout=r?.connection?.timeout??3e3;let a=i?.urlFactory??L,o=this.parseUrl(r?.connection?.url,a)??`https://client-sdk-api.configdirector.com`;this.connectionMode=r?.connection?.mode??`streaming`;let s=this.getTransportConstructor(this.connectionMode);this.telemetryClient=e,this.transport=new s({clientSdkKey:t,baseUrl:o,resolveUrl:a,metaContext:{...r?.metadata,sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:navigator?.userAgent},logger:this.logger,fetch:i?.fetch,connectionRetryDelay:i?.connectionRetryDelay??(e=>2**Math.min(e,9)*1e3),pollingInterval:r?.connection?.pollingInterval}),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})})}getTransportConstructor(e){switch(e){case`one-time`:return I;case`polling`:return F;default:return x}}async initialize(e){this.initializing=!0,await this.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}get context(){return this.currentContext}async connectToTransport(e,t){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.initializing=!1,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.telemetryClient.updateContext(e),this.eventEmitter.emit(`contextUpdated`,{context:e});let r=new Date().getTime()-n,i=this.timeout-r;clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,i>0&&await Promise.race([this.readyPromise,new Promise(e=>{this.timeoutTimer=setTimeout(()=>e(),i)})]).finally(()=>clearTimeout(this.timeoutTimer)),this.ready||this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${t} after ${this.timeout}ms. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`)}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.telemetryClient.evaluatedConfig({contextId:this.currentContext?.id,key:e,defaultValue:n,requestedType:S(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n;let r=w(t,n);return this.telemetryClient.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,t){if(e)try{return t(e)}catch(t){throw new f(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new f(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new f(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}get isInitializing(){return this.initializing}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()}pauseNetwork(){this.logger.debug(`[ConfigDirectorClient] pauseNetwork() called, pausing transport connection`),clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,this.transport.close(),this.ready=!1}async resumeNetwork(){await this.connectToTransport(this.currentContext,`network resume`)}async close(){this.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),clearTimeout(this.timeoutTimer),this.readyResolve?.(),this.telemetryClient.close(),this.transport.close(),this.ready=!1}dispose(){this.clear(),this.close()}};const B=(e,t)=>{if(t===`json`)try{return V(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},V=e=>H(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),H=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t};var U=class{constructor(e){this.logger=e.logger;let t={type:`Initialize`,payload:{sdkKey:e.sdkKey,baseUrl:e.baseUrl.toString(),evaluationQueueLimit:e.evaluationQueueLimit??1e3,initialFlushIntervalDelay:e.initialFlushIntervalDelay??5e3,flushIntervalDelay:e.flushIntervalDelay??3e4}};this.worker=new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,n)=>{if(n===`json`)try{return t(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},t=e=>n(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),n=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t},r=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},i=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},a=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(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},o=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var s=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=e.urlFactory(`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=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return r(e.discreteEvents)&&r(e.aggregatedEvents)&&i(e.droppedEvents)}async sendReport(e){try{let t=await a(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=o(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},c=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}))}},l=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}},u=class{constructor(e){this.aggregator=new c,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new l(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(t,n){return e(t,n)}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)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},d=class extends u{constructor(e){super(e),this.reporter=new s(e)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}};let f=(e,t)=>new URL(e,t?.toString()),p,m=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),h=e=>{try{self.postMessage(e)}catch{self.postMessage(m(e.payload.level,e.payload.message))}},g={debug:function(e,...t){h(m(`debug`,e,t))},info:function(e,...t){h(m(`info`,e,t))},warn:function(e,...t){h(m(`warn`,e,t))},error:function(e,...t){h(m(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!p){g.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(g.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:p=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,logger:g});break;case`EvaluatedConfigEvent`:p?.evaluatedConfig(t.payload);break;case`UpdateContext`:p?.updateContext(t.payload.context);break;case`Flush`:p?.forceFlush();break;case`Close`:(p?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"],{type:`application/javascript`}))),this.worker.onmessage=e=>{this.handleWorkerEvent(e.data)},this.worker.postMessage(t),this.visibilityHandler=()=>{document.visibilityState===`hidden`&&this.flush()};try{document.addEventListener(`visibilitychange`,this.visibilityHandler)}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){let t={type:`UpdateContext`,payload:{context:e}};this.worker.postMessage(t)}evaluatedConfig(e){let t={type:`EvaluatedConfigEvent`,payload:this.sanitizeEvaluatedConfigEvent(e)};this.worker.postMessage(t)}sanitizeEvaluatedConfigEvent(e){return{...e,defaultValue:B(e.defaultValue,e.type),evaluatedValue:B(e.evaluatedValue,e.type)}}handleWorkerEvent(e){if(e.type===`Closed`){this.closeResolve?.();return}if(!e.payload?.level||!e.payload?.message)return;let{level:t,message:n,args:r}=e.payload;switch(t){case`debug`:this.logger.debug(n,...r);break;case`info`:this.logger.info(n,...r);break;case`warn`:this.logger.warn(n,...r);break;case`error`:this.logger.warn(n,...r);break}}flush(){this.worker.postMessage({type:`Flush`})}close(){return this.closePromise?this.closePromise:this.performClose()}performClose(){return document.removeEventListener(`visibilitychange`,this.visibilityHandler),this.closePromise=new Promise(e=>{this.closeResolve=e,this.worker.postMessage({type:`Close`})}),this.closePromise}};const W=(e,t,n)=>{let r=n?.logger??M();return new z(new U({sdkKey:e,logger:r,baseUrl:R(n?.connection?.url)??`https://client-sdk-api.configdirector.com`,urlFactory:L}),e,t,{...n,logger:r})},G=c.default.createContext({status:`loading`});var K=class{decorateMessage(e){return`[ConfigDirector:nextjs-sdk] ${e}`}};const q=e=>M(e,new K);var J=class extends c.Component{constructor(e){super(e),this.state={status:`loading`}}buildClient(){return W(this.props.sdkKey,{sdkName:`nextjs-sdk`,sdkVersion:`0.1.0`},{connection:{url:this.props.url,timeout:this.props.timeout},metadata:{appName:this.props.appName,appVersion:this.props.appVersion},logger:this.props.logger??q(`warn`)})}async componentDidMount(){let e=this.buildClient();e.on(`configsUpdated`,()=>{this.setState({updatedAt:new Date})}),e.on(`clientReady`,()=>{this.setState({status:`ready`})}),this.setState({client:e}),await e.initialize(this.props.context),e.isReady||this.setState({status:`default`})}async componentDidUpdate(e){if(e.context!==this.props.context){let{client:e}=this.state;if(!e)return;this.setState({status:`loading`}),await e.updateContext(this.props.context??{}),e.isReady||this.setState({status:`default`})}}componentWillUnmount(){this.state.client?.dispose()}render(){return(0,l.jsx)(G.Provider,{value:{...this.state,initialConfigs:this.props.initialConfigs},children:this.props.children})}},Y=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorNextContextError`,Object.setPrototypeOf(this,e.prototype)}};const X=()=>new Y(`The ConfigDirector client was not found in the React context. Make sure the component using this hook is inside a <ConfigDirectorProvider>.`),Z=(e,t)=>{let n=(0,c.useContext)(G);if(!n)throw X();return{value:n.client?.isReady?n.client.getValue(e,t):n.initialConfigs?.[e]??t,readyStatus:n.status,loading:n.status===`loading`}},Q=()=>{let e=(0,c.useContext)(G);if(!e)throw X();return{updateContext:async t=>{await e.client?.updateContext(t)}}},$=()=>{let e=(0,c.useContext)(G);if(!e?.client)throw X();return{client:e.client}},ee=()=>{let e=(0,c.useContext)(G);if(!e)throw X();return{readyStatus:e.status,loading:e.status===`loading`}};exports.ConfigDirectorNextContextError=Y,exports.ConfigDirectorProvider=J,exports.createConsoleLogger=q,exports.useClient=$,exports.useConfigDirectorStatus=ee,exports.useConfigValue=Z,exports.useContext=Q;
6
+ `;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}}parseField(e){let t=e.indexOf(`:`);return t===-1?{field:e,value:``}:{field:e.slice(0,t),value:this.extractValue(e,t+1)}}extractValue(e,t){return e[t]===` `?e.slice(t+1):e.slice(t)}};let v=function(e){return e.OPEN=`open`,e.CLOSED=`closed`,e.CONNECTING=`connecting`,e}({});var y=class{constructor(e){this.DEFAULT_SHOULD_RECONNECT=()=>!0,this.DEFAULT_CALCULATE_RECONNECT_DELAY=()=>this.serverReconnectionTime,this.reconnectAttempt=0,this.abortController=new AbortController,this._readyState=v.CLOSED,this.serverReconnectionTime=2e3,this.url=e.url.toString(),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._fetch=e.fetch??((e,t)=>fetch(e,t)),this._onError=this.resolveCallback(e.onError,()=>{},`onError`),this._onConnect=this.resolveCallback(e.onConnect,()=>{},`onConnect`),this._onDisconnect=this.resolveCallback(e.onDisconnect,()=>{},`onDisconnect`),this._onMessage=this.resolveCallback(e.onMessage,()=>{},`onMessage`),this._onComment=this.resolveCallback(e.onComment,()=>{},`onComment`),this._shouldReconnect=this.resolveCallback(e.shouldReconnect,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`),this._calculateReconnectDelay=this.resolveCallback(e.calculateReconnectDelay,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`calculateReconnectDelay`)}set onError(e){this._onError=this.resolveCallback(e,()=>{},`onError`)}set onMessage(e){this._onMessage=this.resolveCallback(e,()=>{},`onMessage`)}set onComment(e){this._onComment=this.resolveCallback(e,()=>{},`onComment`)}set onConnect(e){this._onConnect=this.resolveCallback(e,()=>{},`onConnect`)}set onDisconnect(e){this._onDisconnect=this.resolveCallback(e,()=>{},`onDisconnect`)}set shouldReconnect(e){this._shouldReconnect=this.resolveCallback(e,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`)}set calculateReconnectDelay(e){this._calculateReconnectDelay=this.resolveCallback(e,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`shouldReconnect`)}get readyState(){return this._readyState}connect(){this._readyState===v.CLOSED&&(this.reconnectAttempt=0,this.initiateConnection())}close(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this.abortController.abort()}initiateConnection(){this.abortController=new AbortController,this._readyState=v.CONNECTING,this._fetch(this.url,{...this.requestOptions,headers:this.buildRequestHeaders(),signal:this.abortController.signal,cache:`no-store`}).then(e=>this.handleFetchResponse(e)).catch(e=>{if(e.name===`AbortError`||this.abortController.signal.aborted){this.close();return}this._onError(e),this.scheduleReconnect({error: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 m(`The server response did not have a body`);this._readyState=v.OPEN,this._onConnect(),this.reconnectAttempt=0;let r=new _({onEvent:e=>{typeof e.id==`string`&&(this.lastEventId=e.id),this._onMessage(e)},onComment:e=>this._onComment(e),onRetry:e=>this.serverReconnectionTime=e});await this.readResponseStream(t,r),this.scheduleReconnect({status:n,error:new p(`The server response stream was closed`)})}async readResponseStream(e,t){let n=e.getReader(),r=new TextDecoder,i=!1;for(;!i;){let e=await n.read();e.value&&t.parse(r.decode(e.value,{stream:!0})),i=e.done}t.finish()}disconnected(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this._onDisconnect()}buildRequestHeaders(){let e=this.lastEventId?{"Last-Event-ID":this.lastEventId}:void 0;return{...this.headers,...e}}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.disconnected();return}this._readyState=v.CONNECTING;let n=this._calculateReconnectDelay(t);(!Number.isFinite(n)||n<1||n>1e3*60*60)&&(this._onError(new g(`The calculated reconnect delay is out of range: ${n}. Defaulting to ${this.serverReconnectionTime}`)),n=this.serverReconnectionTime),this.reconnectTimeout=setTimeout(()=>this.initiateConnection(),n)}resolveCallback(e,t,n){if(e==null)return t;if(typeof e!=`function`)throw new h(`${n} must be a function`);return e}};const b=e=>new y(e);var x=class{constructor(e){this.options=e,this.eventEmitter=new u,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/sse/v1`,e.baseUrl)}async connect(e,t){this.eventSource&&this.close();let n=new Promise((t,n)=>{this.eventSource=b({url:this.url,method:`POST`,headers:{"Content-Type":`application/json`},fetch:this.options.fetch,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=this.options.connectionRetryDelay(e.attempt);return this.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${t}ms.`),t},onError:e=>{this.logger.debug(`[EventSourceTransport] Error: `,e)},onDisconnect:()=>{this.logger.debug(`[EventSourceTransport] Disconnected`)}}),this.eventSource.connect()});return clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,Promise.race([n,new Promise(e=>{this.timeoutTimer=setTimeout(()=>e(this),t)})]).finally(()=>clearTimeout(this.timeoutTimer))}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 n=e??0;return new d(`${`Connection failed with status: ${e??`unknown`}.`}${t?` Error: ${t}`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}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(),clearTimeout(this.timeoutTimer)}dispose(){this.close(),this.clear()}};const S=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},C=e=>e===`number`||e===`bigint`,w=(e,t)=>{let n=e.value,r=S(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=T(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(C(typeof t)&&e.type===`integer`){let e=E(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(C(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`}},T=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},E=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},O={off:-1,error:0,warn:1,info:2,debug:3},k=()=>{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 A=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=k()}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){O[this.level]>=O[t]&&e(`[${this.dateFormatter.format(new Date)}] ${this.decorator?.decorateMessage(n)}`,...r)}},j=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const M=(e,t)=>new A(e??`warn`,t??new j);var N=class{constructor(){this.fatalError=!1,this.pollingIntervalSeconds=60}clearPollingInterval(){clearInterval(this.pollingInterval)}schedulePollingInterval(e){clearInterval(this.pollingInterval),!(this.pollingIntervalSeconds<=0)&&(this.pollingInterval=setInterval(e,this.pollingIntervalSeconds*1e3))}async handleNonOkResponse(e){if(!e.ok)throw this.isStatusFatal(e.status)?(this.fatalError=!0,this.prepareFatalResponseStatusError(e.status,await e.text())):new d(`Connection failed with status: ${e.status}`,e.status)}handleFetchError(e){throw this.isFetchErrorFatal(e)?(this.fatalError=!0,this.close(),new d(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new d(`Failed to parse the response from the server: ${e}`):new d(`Connection failed with error: ${e}.`)}prepareFatalResponseStatusError(e,t){let n=e??0;return new d(`${`Connection failed with status: ${e??`unknown`}`}${(t?.trim()?.length??0)>0?` (${t})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,n)}isStatusFatal(e){return!!e&&e>=400&&e<500}isFetchErrorFatal(e){return e?.name===`NotAllowedError`?!0:e instanceof TypeError}get isConnected(){return this.pollingInterval!==void 0}close(){clearInterval(this.pollingInterval),this.pollingInterval=void 0}dispose(){this.close(),this.clear()}};const P=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(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var F=class extends N{constructor(e){super(),this.options=e,this.eventEmitter=new u,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/polling/v1`,e.baseUrl),this.pollingIntervalSeconds=e.pollingInterval??60}async connect(e,t){return this.clearPollingInterval(),await this.fetchConfigs(e,t),this.schedulePollingInterval(()=>{this.fetchConfigs(e,t).catch(e=>{this.logger.error(`[PollingTransport] Error during polling:`,e)})}),this}async fetchConfigs(e,t){if(this.fatalError){this.logger.warn(`[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`);return}try{let n=await P(t,this.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:this.options.metaContext,clientSdkKey:this.options.clientSdkKey,lastUpdateTimestamp:this.lastUpdateTimestamp})},this.logger);if(await this.handleNonOkResponse(n),n.status==200){let e=JSON.parse(await n.text());this.lastUpdateTimestamp=e.timestamp,this.eventEmitter.emit(`configSetReceived`,e)}}catch(e){this.handleFetchError(e)}}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}},I=class extends F{constructor(e){super(e),this.pollingIntervalSeconds=0}};const L=(e,t)=>new URL(e,t?.toString()),R=e=>{if(e)try{return new URL(e)}catch(t){throw new f(`Invalid URL '${e}'. Parsing failed: ${t}`)}};var z=class{constructor(e,t,n,r,i){this.handlersMap=new Map,this.eventEmitter=new u,this.ready=!1,this.initializing=!1,this.logger=r?.logger??M(),this.timeout=r?.connection?.timeout??3e3;let a=i?.urlFactory??L,o=this.parseUrl(r?.connection?.url,a)??`https://client-sdk-api.configdirector.com`;this.connectionMode=r?.connection?.mode??`streaming`;let s=this.getTransportConstructor(this.connectionMode);this.telemetryClient=e,this.transport=new s({clientSdkKey:t,baseUrl:o,resolveUrl:a,metaContext:{...r?.metadata,sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:navigator?.userAgent},logger:this.logger,fetch:i?.fetch,connectionRetryDelay:i?.connectionRetryDelay??(e=>2**Math.min(e,9)*1e3),pollingInterval:r?.connection?.pollingInterval}),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})})}getTransportConstructor(e){switch(e){case`one-time`:return I;case`polling`:return F;default:return x}}async initialize(e){this.initializing=!0,await this.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}get context(){return this.currentContext}async connectToTransport(e,t){try{this.ready=!1,this.readyPromise=new Promise(e=>{this.readyResolve=e}).then(()=>{this.ready=!0,this.initializing=!1,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.telemetryClient.updateContext(e),this.eventEmitter.emit(`contextUpdated`,{context:e});let r=new Date().getTime()-n,i=this.timeout-r;clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,i>0&&await Promise.race([this.readyPromise,new Promise(e=>{this.timeoutTimer=setTimeout(()=>e(),i)})]).finally(()=>clearTimeout(this.timeoutTimer)),this.ready||this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${t} after ${this.timeout}ms. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`)}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.telemetryClient.evaluatedConfig({contextId:this.currentContext?.id,key:e,defaultValue:n,requestedType:S(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n;let r=w(t,n);return this.telemetryClient.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,t){if(e)try{return t(e)}catch(t){throw new f(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new f(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new f(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}get isInitializing(){return this.initializing}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()}pauseNetwork(){this.logger.debug(`[ConfigDirectorClient] pauseNetwork() called, pausing transport connection`),clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,this.transport.close(),this.ready=!1}async resumeNetwork(){await this.connectToTransport(this.currentContext,`network resume`)}async close(){this.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),clearTimeout(this.timeoutTimer),this.readyResolve?.(),this.telemetryClient.close(),this.transport.close(),this.ready=!1}dispose(){this.clear(),this.close()}};const B=(e,t)=>{if(t===`json`)try{return V(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},V=e=>H(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),H=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t};var U=class{constructor(e){this.logger=e.logger;let t={type:`Initialize`,payload:{sdkKey:e.sdkKey,baseUrl:e.baseUrl.toString(),evaluationQueueLimit:e.evaluationQueueLimit??1e3,initialFlushIntervalDelay:e.initialFlushIntervalDelay??5e3,flushIntervalDelay:e.flushIntervalDelay??3e4}};this.worker=new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,n)=>{if(n===`json`)try{return t(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},t=e=>n(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),n=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t},r=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},i=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},a=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(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},o=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var s=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=e.urlFactory(`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=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return r(e.discreteEvents)&&r(e.aggregatedEvents)&&i(e.droppedEvents)}async sendReport(e){try{let t=await a(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=o(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},c=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}))}},l=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}},u=class{constructor(e){this.aggregator=new c,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new l(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(t,n){return e(t,n)}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)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},d=class extends u{constructor(e){super(e),this.reporter=new s(e)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}};let f=(e,t)=>new URL(e,t?.toString()),p,m=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),h=e=>{try{self.postMessage(e)}catch{self.postMessage(m(e.payload.level,e.payload.message))}},g={debug:function(e,...t){h(m(`debug`,e,t))},info:function(e,...t){h(m(`info`,e,t))},warn:function(e,...t){h(m(`warn`,e,t))},error:function(e,...t){h(m(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!p){g.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(g.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:p=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,logger:g});break;case`EvaluatedConfigEvent`:p?.evaluatedConfig(t.payload);break;case`UpdateContext`:p?.updateContext(t.payload.context);break;case`Flush`:p?.forceFlush();break;case`Close`:(p?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"],{type:`application/javascript`}))),this.worker.onmessage=e=>{this.handleWorkerEvent(e.data)},this.worker.postMessage(t),this.visibilityHandler=()=>{document.visibilityState===`hidden`&&this.flush()};try{document.addEventListener(`visibilitychange`,this.visibilityHandler)}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){let t={type:`UpdateContext`,payload:{context:e}};this.worker.postMessage(t)}evaluatedConfig(e){let t={type:`EvaluatedConfigEvent`,payload:this.sanitizeEvaluatedConfigEvent(e)};this.worker.postMessage(t)}sanitizeEvaluatedConfigEvent(e){return{...e,defaultValue:B(e.defaultValue,e.type),evaluatedValue:B(e.evaluatedValue,e.type)}}handleWorkerEvent(e){if(e.type===`Closed`){this.closeResolve?.();return}if(!e.payload?.level||!e.payload?.message)return;let{level:t,message:n,args:r}=e.payload;switch(t){case`debug`:this.logger.debug(n,...r);break;case`info`:this.logger.info(n,...r);break;case`warn`:this.logger.warn(n,...r);break;case`error`:this.logger.warn(n,...r);break}}flush(){this.worker.postMessage({type:`Flush`})}close(){return this.closePromise?this.closePromise:this.performClose()}performClose(){return document.removeEventListener(`visibilitychange`,this.visibilityHandler),this.closePromise=new Promise(e=>{this.closeResolve=e,this.worker.postMessage({type:`Close`})}),this.closePromise}};const W=(e,t,n)=>{let r=n?.logger??M();return new z(new U({sdkKey:e,logger:r,baseUrl:R(n?.connection?.url)??`https://client-sdk-api.configdirector.com`,urlFactory:L}),e,t,{...n,logger:r})},G=c.default.createContext({status:`loading`});var K=class{decorateMessage(e){return`[ConfigDirector:nextjs-sdk] ${e}`}};const q=e=>M(e,new K);var J=class extends c.Component{constructor(e){super(e),this.state={status:`loading`}}buildClient(){return W(this.props.sdkKey,{sdkName:`nextjs-sdk`,sdkVersion:`0.2.0`},{connection:{url:this.props.url,timeout:this.props.timeout},metadata:{appName:this.props.appName,appVersion:this.props.appVersion},logger:q(this.props.logLevel??`warn`)})}async componentDidMount(){let e=this.buildClient();e.on(`configsUpdated`,()=>{this.setState({updatedAt:new Date})}),e.on(`clientReady`,()=>{this.setState({status:`ready`})}),this.setState({client:e}),await e.initialize(this.props.context),e.isReady||this.setState({status:`default`})}async componentDidUpdate(e){if(e.context!==this.props.context){let{client:e}=this.state;if(!e)return;this.setState({status:`loading`}),await e.updateContext(this.props.context??{}),e.isReady||this.setState({status:`default`})}}componentWillUnmount(){this.state.client?.dispose()}render(){return(0,l.jsx)(G.Provider,{value:{...this.state,initialConfigs:this.props.initialConfigs},children:this.props.children})}},Y=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorNextContextError`,Object.setPrototypeOf(this,e.prototype)}};const X=()=>new Y(`The ConfigDirector client was not found in the React context. Make sure the component using this hook is inside a <ConfigDirectorProvider>.`),Z=(e,t)=>{let n=(0,c.useContext)(G);if(!n)throw X();let r=n.initialConfigs?.[e];return{value:n.client?.isReady?n.client.getValue(e,t):r?w(r,t).parsedValue:t,readyStatus:n.status,loading:n.status===`loading`}},Q=()=>{let e=(0,c.useContext)(G);if(!e)throw X();return{updateContext:async t=>{await e.client?.updateContext(t)}}},$=()=>{let e=(0,c.useContext)(G);if(!e?.client)throw X();return{client:e.client}},ee=()=>{let e=(0,c.useContext)(G);if(!e)throw X();return{readyStatus:e.status,loading:e.status===`loading`}};exports.ConfigDirectorNextContextError=Y,exports.ConfigDirectorProvider=J,exports.createConsoleLogger=q,exports.useClient=$,exports.useConfigDirectorStatus=ee,exports.useConfigValue=Z,exports.useContext=Q;
@@ -13,10 +13,18 @@ interface EventProvider<TEventsRecord extends EventsRecord> {
13
13
  }
14
14
  //#endregion
15
15
  //#region ../shared/src/types.d.ts
16
+ declare const ConfigTypeList: readonly ["custom", "boolean", "string", "integer", "float", "enum", "url", "json"];
17
+ type ConfigType = (typeof ConfigTypeList)[number];
16
18
  type ConfigEnumLikeType = {
17
19
  [key: string]: string | number;
18
20
  };
19
21
  type ConfigValueType = string | number | boolean | object | ConfigEnumLikeType;
22
+ type ConfigState = {
23
+ id: string;
24
+ key: string;
25
+ type: ConfigType;
26
+ value: string | undefined | null;
27
+ };
20
28
  type ConfigDirectorLoggingLevel = "debug" | "info" | "warn" | "error" | "off";
21
29
  interface ConfigDirectorLogger {
22
30
  debug(message: string, ...args: any): void;
@@ -180,7 +188,7 @@ interface ConfigDirectorContextData {
180
188
  client?: ConfigDirectorClient;
181
189
  updatedAt?: Date;
182
190
  status: ClientStatus;
183
- initialConfigs?: Record<string, unknown>;
191
+ initialConfigs?: Record<string, ConfigState>;
184
192
  }
185
193
  type ConfigDirectorProviderState = Omit<ConfigDirectorContextData, "initialConfigs">;
186
194
  type ConfigDirectorProviderOptions = {
@@ -203,57 +211,30 @@ type ConfigDirectorProviderOptions = {
203
211
  */
204
212
  context?: ConfigDirectorContext;
205
213
  /**
206
- * A logger instance created with {@link createConsoleLogger}.
214
+ * Log level for the console logger. Defaults to "warn".
207
215
  */
208
- logger?: ConfigDirectorLogger;
216
+ logLevel?: ConfigDirectorLoggingLevel;
209
217
  /**
210
- * Pre-evaluated config values from the server, used to hydrate client components correctly
211
- * during SSR. Obtain these by calling {@link createSsrClient} in your Server Component
212
- * layout and passing the results here.
218
+ * Pre-evaluated config states from the server, used to hydrate client components correctly
219
+ * during SSR. Obtain these via {@link generateSsrConfigSet} or by using the
220
+ * `ConfigDirectorProvider` exported from `@configdirector/nextjs-sdk/server`, which
221
+ * populates this automatically.
213
222
  *
214
223
  * When the browser client is not yet ready (during SSR and the initial client render before
215
224
  * initialization completes), hooks will return values from this map rather than the default
216
225
  * value, avoiding a flash of wrong content on hydration.
217
226
  */
218
- initialConfigs?: Record<string, unknown>;
227
+ initialConfigs?: Record<string, ConfigState>;
219
228
  };
220
229
  //#endregion
221
230
  //#region src/client/ConfigDirectorProvider.d.ts
222
231
  /**
223
232
  * Initializes the ConfigDirector browser client and provides it to all descendant hooks.
224
233
  *
225
- * Place this in your root layout, wrapping your application. Pass server-evaluated
226
- * `initialConfigs` to ensure Client Components render the correct values during SSR and before
227
- * the browser client finishes initializing, preventing a flash of wrong content on hydration:
228
- *
229
- * ```tsx
230
- * // app/layout.tsx
231
- * import { createSsrClient } from "@configdirector/nextjs-sdk/server";
232
- * import { ConfigDirectorProvider } from "@configdirector/nextjs-sdk/client";
233
- * import { cookies } from "next/headers";
234
- *
235
- * export default async function RootLayout({ children }) {
236
- * const userId = (await cookies()).get("userId")?.value;
237
- * const ssrClient = createSsrClient({ userId });
238
- *
239
- * return (
240
- * <html>
241
- * <body>
242
- * <ConfigDirectorProvider
243
- * sdkKey={process.env.NEXT_PUBLIC_CONFIGDIRECTOR_CLIENT_KEY!}
244
- * context={{ userId }}
245
- * initialConfigs={{
246
- * "new-dashboard": ssrClient.getValue("new-dashboard", false),
247
- * "theme": ssrClient.getValue("theme", "light"),
248
- * }}
249
- * >
250
- * {children}
251
- * </ConfigDirectorProvider>
252
- * </body>
253
- * </html>
254
- * );
255
- * }
256
- * ```
234
+ * Place this in your root layout, wrapping your application. In most cases prefer the
235
+ * `ConfigDirectorProvider` exported from `@configdirector/nextjs-sdk/server` — it is a React
236
+ * Server Component that populates `initialConfigs` automatically, ensuring Client Components
237
+ * render the correct values during SSR without any extra wiring.
257
238
  */
258
239
  declare class ConfigDirectorProvider extends Component<PropsWithChildren<ConfigDirectorProviderOptions>, ConfigDirectorProviderState> {
259
240
  constructor(props: PropsWithChildren<ConfigDirectorProviderOptions>);
@@ -320,5 +301,5 @@ declare class ConfigDirectorNextContextError extends Error {
320
301
  constructor(message: string);
321
302
  }
322
303
  //#endregion
323
- export { type ClientStatus, type ConfigDirectorClient, type ConfigDirectorContext, type ConfigDirectorLogger, ConfigDirectorNextContextError, ConfigDirectorProvider, type ConfigDirectorProviderOptions, type ConfigValueType, createConsoleLogger, useClient, useConfigDirectorStatus, useConfigValue, useContext };
304
+ export { type ClientStatus, type ConfigDirectorClient, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorNextContextError, ConfigDirectorProvider, type ConfigDirectorProviderOptions, type ConfigValueType, createConsoleLogger, useClient, useConfigDirectorStatus, useConfigValue, useContext };
324
305
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../js-client-core/src/Emitter.ts","../../../shared/src/types.ts","../../../js-client-core/src/types.ts","../../src/client/types.ts","../../src/client/ConfigDirectorProvider.tsx","../../src/client/hooks.tsx","../../src/client/logger.ts","../../src/client/errors.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../../js-client-core/src/Emitter.ts","../../../shared/src/types.ts","../../../js-client-core/src/types.ts","../../src/client/types.ts","../../src/client/ConfigDirectorProvider.tsx","../../src/client/hooks.tsx","../../src/client/logger.ts","../../src/client/errors.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;;;cCVW,cAAA;AAAA,KAUD,UAAA,WAAqB,cAAA;AAAA,KAErB,kBAAA;EAAA,CAAwB,GAAA;AAAA;AAAA,KAExB,eAAA,wCAAuD,kBAAA;AAAA,KAEvD,WAAA;EACV,EAAA;EACA,GAAA;EACA,IAAA,EAAM,UAAA;EACN,KAAA;AAAA;AAAA,KAQU,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;;;;;KAoBhB,qBAAA;EDlDK;;;;;;;;EC2Df,EAAA;EDzDwB;;;;EC+DxB,IAAA;ED/DqE;;;;ECqErE,MAAA;IAAA,CAAY,GAAA;EAAA;AAAA;;;KCmBF,mBAAA;AAAA,KAEA,YAAA;EACV,cAAA;IAAkB,IAAA;EAAA;EAClB,WAAA;IAAe,MAAA,EAAQ,mBAAA;EAAA;EACvB,cAAA;IAAkB,OAAA,EAAS,qBAAA;EAAA;AAAA;AAAA,KAGjB,YAAA,WAAuB,eAAA,KAAoB,OAAA,EAAS,CAAA;;;;;;;;;;UAW/C,oBAAA,SAA6B,aAAA,CAAc,YAAA;EDxFhD;;;;;AAEZ;;;;;;;;ECoGE,UAAA,CAAW,OAAA,GAAU,qBAAA,GAAwB,OAAA;EDjGpB;;;;;ECwGzB,aAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;EDpGrB;;;AAoB5B;;;;;EApB4B,IC8GtB,OAAA,IAAW,qBAAA;EDrEf;;;;;;EAAA,IC6EI,OAAA;EA1DM;;;;EAAA,IAgEN,cAAA;EA9DM;;;;;;;;;EAyEV,QAAA,WAAmB,eAAA,EAAiB,SAAA,UAAmB,YAAA,EAAc,CAAA,GAAI,CAAA;EAtEvD;;;;AAGpB;;;;;;;EAgFE,KAAA,WAAgB,eAAA,EAAiB,SAAA,UAAmB,YAAA,EAAc,CAAA,EAAG,QAAA,EAAU,YAAA,CAAa,CAAA;EAhF7B;;AAWjE;;;;;;EA+EE,OAAA,WAAkB,eAAA,EAAiB,SAAA,UAAmB,QAAA,GAAW,YAAA,CAAa,CAAA;EA1D/B;;;EA+D/C,UAAA;EA5ByE;;;;;;;EAqCzE,YAAA;EAMiB;;;;EAAjB,aAAA,IAAiB,OAAA;EAnGyC;;;;;;EA2G1D,OAAA;AAAA;;;KC1NU,YAAA;AAAA,UAEK,yBAAA;EACf,MAAA,GAAS,oBAAA;EACT,SAAA,GAAY,IAAA;EACZ,MAAA,EAAQ,YAAA;EACR,cAAA,GAAiB,MAAA,SAAe,WAAA;AAAA;AAAA,KAGtB,2BAAA,GAA8B,IAAA,CAAK,yBAAA;AAAA,KAEnC,6BAAA;EHfQ;;AAAgB;EGmBlC,MAAA;EACA,OAAA;EACA,UAAA;EHnBkE;;;EGuBlE,GAAA;EHtB2B;;;EG0B3B,OAAA;EH3BkE;;;EG+BlE,OAAA,GAAU,qBAAA;EH9BkB;;AAG9B;EG+BE,QAAA,GAAW,0BAAA;EH/BiB;;;;;;;;;;EG0C5B,cAAA,GAAiB,MAAA,SAAe,WAAA;AAAA;;;;;;;;;;AHjDpB;cIgBD,sBAAA,SAA+B,SAAA,CAC1C,iBAAA,CAAkB,6BAAA,GAClB,2BAAA;cAEY,KAAA,EAAO,iBAAA,CAAkB,6BAAA;EAAA,QAK7B,WAAA;EAYO,iBAAA,CAAA,GAAqB,OAAA;EAkBrB,kBAAA,CACb,SAAA,EAAW,iBAAA,CAAkB,6BAAA,IAC5B,OAAA;EAYM,oBAAA,CAAA;EAIA,MAAA,CAAA,GAAM,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;;;;;;;AJzEH;;;cKyBD,cAAA,aAA4B,eAAA,EACvC,GAAA,UACA,YAAA,EAAc,CAAA;EACX,KAAA,EAAO,CAAA;EAAG,WAAA,EAAa,YAAA;EAAc,OAAA;AAAA;;;;;;;cA2B7B,UAAA;EAAmB,aAAA,GAAgB,OAAA,EAAS,qBAAA,KAA0B,OAAA;AAAA;;;;;;cAiBtE,SAAA;UAIZ,oBAAA;AAAA;;;;;;;;;;cAWY,uBAAA;EAAgC,WAAA,EAAa,YAAA;EAAc,OAAA;AAAA;;;cC1E3D,mBAAA,GAAuB,KAAA,EAAO,0BAAA,KAA6B,oBAAA;;;cCb3D,8BAAA,SAAuC,KAAA;EAAA,SACzB,IAAA;cAEb,OAAA;AAAA"}
@@ -13,10 +13,18 @@ interface EventProvider<TEventsRecord extends EventsRecord> {
13
13
  }
14
14
  //#endregion
15
15
  //#region ../shared/src/types.d.ts
16
+ declare const ConfigTypeList: readonly ["custom", "boolean", "string", "integer", "float", "enum", "url", "json"];
17
+ type ConfigType = (typeof ConfigTypeList)[number];
16
18
  type ConfigEnumLikeType = {
17
19
  [key: string]: string | number;
18
20
  };
19
21
  type ConfigValueType = string | number | boolean | object | ConfigEnumLikeType;
22
+ type ConfigState = {
23
+ id: string;
24
+ key: string;
25
+ type: ConfigType;
26
+ value: string | undefined | null;
27
+ };
20
28
  type ConfigDirectorLoggingLevel = "debug" | "info" | "warn" | "error" | "off";
21
29
  interface ConfigDirectorLogger {
22
30
  debug(message: string, ...args: any): void;
@@ -180,7 +188,7 @@ interface ConfigDirectorContextData {
180
188
  client?: ConfigDirectorClient;
181
189
  updatedAt?: Date;
182
190
  status: ClientStatus;
183
- initialConfigs?: Record<string, unknown>;
191
+ initialConfigs?: Record<string, ConfigState>;
184
192
  }
185
193
  type ConfigDirectorProviderState = Omit<ConfigDirectorContextData, "initialConfigs">;
186
194
  type ConfigDirectorProviderOptions = {
@@ -203,57 +211,30 @@ type ConfigDirectorProviderOptions = {
203
211
  */
204
212
  context?: ConfigDirectorContext;
205
213
  /**
206
- * A logger instance created with {@link createConsoleLogger}.
214
+ * Log level for the console logger. Defaults to "warn".
207
215
  */
208
- logger?: ConfigDirectorLogger;
216
+ logLevel?: ConfigDirectorLoggingLevel;
209
217
  /**
210
- * Pre-evaluated config values from the server, used to hydrate client components correctly
211
- * during SSR. Obtain these by calling {@link createSsrClient} in your Server Component
212
- * layout and passing the results here.
218
+ * Pre-evaluated config states from the server, used to hydrate client components correctly
219
+ * during SSR. Obtain these via {@link generateSsrConfigSet} or by using the
220
+ * `ConfigDirectorProvider` exported from `@configdirector/nextjs-sdk/server`, which
221
+ * populates this automatically.
213
222
  *
214
223
  * When the browser client is not yet ready (during SSR and the initial client render before
215
224
  * initialization completes), hooks will return values from this map rather than the default
216
225
  * value, avoiding a flash of wrong content on hydration.
217
226
  */
218
- initialConfigs?: Record<string, unknown>;
227
+ initialConfigs?: Record<string, ConfigState>;
219
228
  };
220
229
  //#endregion
221
230
  //#region src/client/ConfigDirectorProvider.d.ts
222
231
  /**
223
232
  * Initializes the ConfigDirector browser client and provides it to all descendant hooks.
224
233
  *
225
- * Place this in your root layout, wrapping your application. Pass server-evaluated
226
- * `initialConfigs` to ensure Client Components render the correct values during SSR and before
227
- * the browser client finishes initializing, preventing a flash of wrong content on hydration:
228
- *
229
- * ```tsx
230
- * // app/layout.tsx
231
- * import { createSsrClient } from "@configdirector/nextjs-sdk/server";
232
- * import { ConfigDirectorProvider } from "@configdirector/nextjs-sdk/client";
233
- * import { cookies } from "next/headers";
234
- *
235
- * export default async function RootLayout({ children }) {
236
- * const userId = (await cookies()).get("userId")?.value;
237
- * const ssrClient = createSsrClient({ userId });
238
- *
239
- * return (
240
- * <html>
241
- * <body>
242
- * <ConfigDirectorProvider
243
- * sdkKey={process.env.NEXT_PUBLIC_CONFIGDIRECTOR_CLIENT_KEY!}
244
- * context={{ userId }}
245
- * initialConfigs={{
246
- * "new-dashboard": ssrClient.getValue("new-dashboard", false),
247
- * "theme": ssrClient.getValue("theme", "light"),
248
- * }}
249
- * >
250
- * {children}
251
- * </ConfigDirectorProvider>
252
- * </body>
253
- * </html>
254
- * );
255
- * }
256
- * ```
234
+ * Place this in your root layout, wrapping your application. In most cases prefer the
235
+ * `ConfigDirectorProvider` exported from `@configdirector/nextjs-sdk/server` — it is a React
236
+ * Server Component that populates `initialConfigs` automatically, ensuring Client Components
237
+ * render the correct values during SSR without any extra wiring.
257
238
  */
258
239
  declare class ConfigDirectorProvider extends Component<PropsWithChildren<ConfigDirectorProviderOptions>, ConfigDirectorProviderState> {
259
240
  constructor(props: PropsWithChildren<ConfigDirectorProviderOptions>);
@@ -320,5 +301,5 @@ declare class ConfigDirectorNextContextError extends Error {
320
301
  constructor(message: string);
321
302
  }
322
303
  //#endregion
323
- export { type ClientStatus, type ConfigDirectorClient, type ConfigDirectorContext, type ConfigDirectorLogger, ConfigDirectorNextContextError, ConfigDirectorProvider, type ConfigDirectorProviderOptions, type ConfigValueType, createConsoleLogger, useClient, useConfigDirectorStatus, useConfigValue, useContext };
304
+ export { type ClientStatus, type ConfigDirectorClient, type ConfigDirectorContext, type ConfigDirectorLogger, type ConfigDirectorLoggingLevel, ConfigDirectorNextContextError, ConfigDirectorProvider, type ConfigDirectorProviderOptions, type ConfigValueType, createConsoleLogger, useClient, useConfigDirectorStatus, useConfigValue, useContext };
324
305
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../js-client-core/src/Emitter.ts","../../../shared/src/types.ts","../../../js-client-core/src/types.ts","../../src/client/types.ts","../../src/client/ConfigDirectorProvider.tsx","../../src/client/hooks.tsx","../../src/client/logger.ts","../../src/client/errors.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../js-client-core/src/Emitter.ts","../../../shared/src/types.ts","../../../js-client-core/src/types.ts","../../src/client/types.ts","../../src/client/ConfigDirectorProvider.tsx","../../src/client/hooks.tsx","../../src/client/logger.ts","../../src/client/errors.ts"],"mappings":"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"}
@@ -3,5 +3,5 @@ import e,{Component as t,useContext as n}from"react";import{jsx as r}from"react/
3
3
  `?(t.push(e.slice(n,r)),i===`\r`&&e[r+1]===`
4
4
  `&&r++,r++,n=r):r++}return this.bufferedInput=e.slice(n),t}dispatchLine(e){if(e.startsWith(`:`)){var t;(t=this.onComment)==null||t.call(this,this.extractValue(e,1));return}if(e===``){this.emitEvent();return}this.applyField(this.parseField(e))}emitEvent(){let e=this.currentEvent.data.endsWith(`
5
5
  `)?this.currentEvent.data.slice(0,-1):this.currentEvent.data;e!==``&&this.onEvent(g(g({},this.currentEvent),{},{id:this.lastEventId,data:e})),this.currentEvent={data:``}}applyField(e){switch(e.field){case`event`:this.currentEvent.type=e.value;break;case`data`:this.currentEvent.data+=e.value+`
6
- `;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}}parseField(e){let t=e.indexOf(`:`);return t===-1?{field:e,value:``}:{field:e.slice(0,t),value:this.extractValue(e,t+1)}}extractValue(e,t){return e[t]===` `?e.slice(t+1):e.slice(t)}};let v=function(e){return e.OPEN=`open`,e.CLOSED=`closed`,e.CONNECTING=`connecting`,e}({});var y=class{constructor(e){var t,n;this.DEFAULT_SHOULD_RECONNECT=()=>!0,this.DEFAULT_CALCULATE_RECONNECT_DELAY=()=>this.serverReconnectionTime,this.reconnectAttempt=0,this.abortController=new AbortController,this._readyState=v.CLOSED,this.serverReconnectionTime=2e3,this.url=e.url.toString(),this.lastEventId=e.lastEventId,this.headers=g({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._fetch=(n=e.fetch)==null?((e,t)=>fetch(e,t)):n,this._onError=this.resolveCallback(e.onError,()=>{},`onError`),this._onConnect=this.resolveCallback(e.onConnect,()=>{},`onConnect`),this._onDisconnect=this.resolveCallback(e.onDisconnect,()=>{},`onDisconnect`),this._onMessage=this.resolveCallback(e.onMessage,()=>{},`onMessage`),this._onComment=this.resolveCallback(e.onComment,()=>{},`onComment`),this._shouldReconnect=this.resolveCallback(e.shouldReconnect,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`),this._calculateReconnectDelay=this.resolveCallback(e.calculateReconnectDelay,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`calculateReconnectDelay`)}set onError(e){this._onError=this.resolveCallback(e,()=>{},`onError`)}set onMessage(e){this._onMessage=this.resolveCallback(e,()=>{},`onMessage`)}set onComment(e){this._onComment=this.resolveCallback(e,()=>{},`onComment`)}set onConnect(e){this._onConnect=this.resolveCallback(e,()=>{},`onConnect`)}set onDisconnect(e){this._onDisconnect=this.resolveCallback(e,()=>{},`onDisconnect`)}set shouldReconnect(e){this._shouldReconnect=this.resolveCallback(e,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`)}set calculateReconnectDelay(e){this._calculateReconnectDelay=this.resolveCallback(e,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`shouldReconnect`)}get readyState(){return this._readyState}connect(){this._readyState===v.CLOSED&&(this.reconnectAttempt=0,this.initiateConnection())}close(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this.abortController.abort()}initiateConnection(){this.abortController=new AbortController,this._readyState=v.CONNECTING,this._fetch(this.url,g(g({},this.requestOptions),{},{headers:this.buildRequestHeaders(),signal:this.abortController.signal,cache:`no-store`})).then(e=>this.handleFetchResponse(e)).catch(e=>{if(e.name===`AbortError`||this.abortController.signal.aborted){this.close();return}this._onError(e),this.scheduleReconnect({error: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 c(`The server response did not have a body`);t._readyState=v.OPEN,t._onConnect(),t.reconnectAttempt=0;let i=new _({onEvent:e=>{typeof e.id==`string`&&(t.lastEventId=e.id),t._onMessage(e)},onComment:e=>t._onComment(e),onRetry:e=>t.serverReconnectionTime=e});await t.readResponseStream(n,i),t.scheduleReconnect({status:r,error:new s(`The server response stream was closed`)})}async readResponseStream(e,t){let n=e.getReader(),r=new TextDecoder,i=!1;for(;!i;){let e=await n.read();e.value&&t.parse(r.decode(e.value,{stream:!0})),i=e.done}t.finish()}disconnected(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this._onDisconnect()}buildRequestHeaders(){let e=this.lastEventId?{"Last-Event-ID":this.lastEventId}:void 0;return g(g({},this.headers),e)}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.disconnected();return}this._readyState=v.CONNECTING;let n=this._calculateReconnectDelay(t);(!Number.isFinite(n)||n<1||n>1e3*60*60)&&(this._onError(new u(`The calculated reconnect delay is out of range: ${n}. Defaulting to ${this.serverReconnectionTime}`)),n=this.serverReconnectionTime),this.reconnectTimeout=setTimeout(()=>this.initiateConnection(),n)}resolveCallback(e,t,n){if(e==null)return t;if(typeof e!=`function`)throw new l(`${n} must be a function`);return e}};const b=e=>new y(e);var x=class{constructor(e){this.options=e,this.eventEmitter=new i,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/sse/v1`,e.baseUrl)}async connect(e,t){var n=this;n.eventSource&&n.close();let r=new Promise((t,r)=>{n.eventSource=b({url:n.url,method:`POST`,headers:{"Content-Type":`application/json`},fetch:n.options.fetch,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=n.options.connectionRetryDelay(e.attempt);return n.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${t}ms.`),t},onError:e=>{n.logger.debug(`[EventSourceTransport] Error: `,e)},onDisconnect:()=>{n.logger.debug(`[EventSourceTransport] Disconnected`)}}),n.eventSource.connect()});return clearTimeout(n.timeoutTimer),n.timeoutTimer=void 0,Promise.race([r,new Promise(e=>{n.timeoutTimer=setTimeout(()=>e(n),t)})]).finally(()=>clearTimeout(n.timeoutTimer))}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 n=e==null?0:e;return new a(`${`Connection failed with status: ${e==null?`unknown`:e}.`}${t?` Error: ${t}`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}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(),clearTimeout(this.timeoutTimer)}dispose(){this.close(),this.clear()}};const S=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},C=e=>e===`number`||e===`bigint`,w=(e,t)=>{let n=e.value,r=S(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=T(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(C(typeof t)&&e.type===`integer`){let e=E(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(C(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`}},T=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},E=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},O={off:-1,error:0,warn:1,info:2,debug:3},k=()=>{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,g(g({},e),{},{fractionalSecondDigits:3}))}catch(t){return new Intl.DateTimeFormat(void 0,e)}};var A=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=k()}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(O[this.level]>=O[t]){var i;e(`[${this.dateFormatter.format(new Date)}] ${(i=this.decorator)==null?void 0:i.decorateMessage(n)}`,...r)}}},j=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const M=(e,t)=>new A(e==null?`warn`:e,t==null?new j:t);var N=class{constructor(){this.fatalError=!1,this.pollingIntervalSeconds=60}clearPollingInterval(){clearInterval(this.pollingInterval)}schedulePollingInterval(e){clearInterval(this.pollingInterval),!(this.pollingIntervalSeconds<=0)&&(this.pollingInterval=setInterval(e,this.pollingIntervalSeconds*1e3))}async handleNonOkResponse(e){var t=this;if(!e.ok)throw t.isStatusFatal(e.status)?(t.fatalError=!0,t.prepareFatalResponseStatusError(e.status,await e.text())):new a(`Connection failed with status: ${e.status}`,e.status)}handleFetchError(e){throw this.isFetchErrorFatal(e)?(this.fatalError=!0,this.close(),new a(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new a(`Failed to parse the response from the server: ${e}`):new a(`Connection failed with error: ${e}.`)}prepareFatalResponseStatusError(e,t){var n,r;let i=e==null?0:e;return new a(`${`Connection failed with status: ${e==null?`unknown`:e}`}${((n=t==null||(r=t.trim())==null?void 0:r.length)==null?0:n)>0?` (${t})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,i)}isStatusFatal(e){return!!e&&e>=400&&e<500}isFetchErrorFatal(e){return(e==null?void 0:e.name)===`NotAllowedError`?!0:e instanceof TypeError}get isConnected(){return this.pollingInterval!==void 0}close(){clearInterval(this.pollingInterval),this.pollingInterval=void 0}dispose(){this.close(),this.clear()}};const P=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(typeof t==`string`||t instanceof Request?t:t.toString(),g(g({},n),{},{signal:i.signal}));return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var F=class extends N{constructor(e){var t;super(),this.options=e,this.eventEmitter=new i,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/polling/v1`,e.baseUrl),this.pollingIntervalSeconds=(t=e.pollingInterval)==null?60:t}async connect(e,t){var n=this;return n.clearPollingInterval(),await n.fetchConfigs(e,t),n.schedulePollingInterval(()=>{n.fetchConfigs(e,t).catch(e=>{n.logger.error(`[PollingTransport] Error during polling:`,e)})}),n}async fetchConfigs(e,t){var n=this;if(n.fatalError){n.logger.warn(`[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`);return}try{let r=await P(t,n.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:n.options.metaContext,clientSdkKey:n.options.clientSdkKey,lastUpdateTimestamp:n.lastUpdateTimestamp})},n.logger);if(await n.handleNonOkResponse(r),r.status==200){let e=JSON.parse(await r.text());n.lastUpdateTimestamp=e.timestamp,n.eventEmitter.emit(`configSetReceived`,e)}}catch(e){n.handleFetchError(e)}}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}},I=class extends F{constructor(e){super(e),this.pollingIntervalSeconds=0}};const L=(e,t)=>new URL(e,t==null?void 0:t.toString()),R=e=>{if(e)try{return new URL(e)}catch(t){throw new o(`Invalid URL '${e}'. Parsing failed: ${t}`)}},z=`https://client-sdk-api.configdirector.com`;var B=class{constructor(e,t,n,r,a){var o,s,c,l,u,d,f,p,m,h,_;this.handlersMap=new Map,this.eventEmitter=new i,this.ready=!1,this.initializing=!1,this.logger=(o=r==null?void 0:r.logger)==null?M():o,this.timeout=(s=r==null||(c=r.connection)==null?void 0:c.timeout)==null?3e3:s;let v=(l=a==null?void 0:a.urlFactory)==null?L:l,y=(u=this.parseUrl(r==null||(d=r.connection)==null?void 0:d.url,v))==null?z:u;this.connectionMode=(f=r==null||(p=r.connection)==null?void 0:p.mode)==null?`streaming`:f;let b=this.getTransportConstructor(this.connectionMode);this.telemetryClient=e,this.transport=new b({clientSdkKey:t,baseUrl:y,resolveUrl:v,metaContext:g(g({},r==null?void 0:r.metadata),{},{sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:(m=navigator)==null?void 0:m.userAgent}),logger:this.logger,fetch:a==null?void 0:a.fetch,connectionRetryDelay:(h=a==null?void 0:a.connectionRetryDelay)==null?(e=>2**Math.min(e,9)*1e3):h,pollingInterval:r==null||(_=r.connection)==null?void 0:_.pollingInterval}),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=g(g({},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})})}getTransportConstructor(e){switch(e){case`one-time`:return I;case`polling`:return F;default:return x}}async initialize(e){var t=this;t.initializing=!0,await t.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}get context(){return this.currentContext}async connectToTransport(e,t){var n=this;try{n.ready=!1,n.readyPromise=new Promise(e=>{n.readyResolve=e}).then(()=>{n.ready=!0,n.initializing=!1,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.telemetryClient.updateContext(e),n.eventEmitter.emit(`contextUpdated`,{context:e});let i=new Date().getTime()-r,a=n.timeout-i;clearTimeout(n.timeoutTimer),n.timeoutTimer=void 0,a>0&&await Promise.race([n.readyPromise,new Promise(e=>{n.timeoutTimer=setTimeout(()=>e(),a)})]).finally(()=>clearTimeout(n.timeoutTimer)),n.ready||n.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${t} after ${n.timeout}ms. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`)}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.telemetryClient.evaluatedConfig({contextId:(i=this.currentContext)==null?void 0:i.id,key:e,defaultValue:n,requestedType:S(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n}let a=w(t,n);return this.telemetryClient.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,t){if(e)try{return t(e)}catch(t){throw new o(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new o(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new o(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}get isInitializing(){return this.initializing}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()}pauseNetwork(){this.logger.debug(`[ConfigDirectorClient] pauseNetwork() called, pausing transport connection`),clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,this.transport.close(),this.ready=!1}async resumeNetwork(){var e=this;await e.connectToTransport(e.currentContext,`network resume`)}async close(){var e=this,t;e.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),clearTimeout(e.timeoutTimer),(t=e.readyResolve)==null||t.call(e),e.telemetryClient.close(),e.transport.close(),e.ready=!1}dispose(){this.clear(),this.close()}};const V=(e,t)=>{if(t===`json`)try{return H(JSON.stringify(e))}catch(t){return e.toString().slice(0,500)}return e.toString().slice(0,500)},H=e=>U(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),U=e=>{let t=5381;for(let r=0;r<e.length;r++){var n;t=(t<<5)+t+((n=e[r])==null?0:n),t>>>=0}return t};var W=class{constructor(e){var t,n,r;this.logger=e.logger;let i={type:`Initialize`,payload:{sdkKey:e.sdkKey,baseUrl:e.baseUrl.toString(),evaluationQueueLimit:(t=e.evaluationQueueLimit)==null?1e3:t,initialFlushIntervalDelay:(n=e.initialFlushIntervalDelay)==null?5e3:n,flushIntervalDelay:(r=e.flushIntervalDelay)==null?3e4:r}};this.worker=new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,n)=>{if(n===`json`)try{return t(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},t=e=>n(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),n=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t},r=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},i=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},a=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(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},o=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var s=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=e.urlFactory(`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=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return r(e.discreteEvents)&&r(e.aggregatedEvents)&&i(e.droppedEvents)}async sendReport(e){try{let t=await a(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=o(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},c=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}))}},l=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}},u=class{constructor(e){this.aggregator=new c,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new l(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(t,n){return e(t,n)}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)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},d=class extends u{constructor(e){super(e),this.reporter=new s(e)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}};let f=(e,t)=>new URL(e,t?.toString()),p,m=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),h=e=>{try{self.postMessage(e)}catch{self.postMessage(m(e.payload.level,e.payload.message))}},g={debug:function(e,...t){h(m(`debug`,e,t))},info:function(e,...t){h(m(`info`,e,t))},warn:function(e,...t){h(m(`warn`,e,t))},error:function(e,...t){h(m(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!p){g.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(g.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:p=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,logger:g});break;case`EvaluatedConfigEvent`:p?.evaluatedConfig(t.payload);break;case`UpdateContext`:p?.updateContext(t.payload.context);break;case`Flush`:p?.forceFlush();break;case`Close`:(p?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"],{type:`application/javascript`}))),this.worker.onmessage=e=>{this.handleWorkerEvent(e.data)},this.worker.postMessage(i),this.visibilityHandler=()=>{document.visibilityState===`hidden`&&this.flush()};try{document.addEventListener(`visibilitychange`,this.visibilityHandler)}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){var t=this;let n={type:`UpdateContext`,payload:{context:e}};t.worker.postMessage(n)}evaluatedConfig(e){let t={type:`EvaluatedConfigEvent`,payload:this.sanitizeEvaluatedConfigEvent(e)};this.worker.postMessage(t)}sanitizeEvaluatedConfigEvent(e){return g(g({},e),{},{defaultValue:V(e.defaultValue,e.type),evaluatedValue:V(e.evaluatedValue,e.type)})}handleWorkerEvent(e){var t,n;if(e.type===`Closed`){var r;(r=this.closeResolve)==null||r.call(this);return}if(!((t=e.payload)!=null&&t.level)||!((n=e.payload)!=null&&n.message))return;let{level:i,message:a,args:o}=e.payload;switch(i){case`debug`:this.logger.debug(a,...o);break;case`info`:this.logger.info(a,...o);break;case`warn`:this.logger.warn(a,...o);break;case`error`:this.logger.warn(a,...o);break}}flush(){this.worker.postMessage({type:`Flush`})}close(){return this.closePromise?this.closePromise:this.performClose()}performClose(){return document.removeEventListener(`visibilitychange`,this.visibilityHandler),this.closePromise=new Promise(e=>{this.closeResolve=e,this.worker.postMessage({type:`Close`})}),this.closePromise}};const G=(e,t,n)=>{var r,i,a;let o=(r=n==null?void 0:n.logger)==null?M():r;return new B(new W({sdkKey:e,logger:o,baseUrl:(i=R(n==null||(a=n.connection)==null?void 0:a.url))==null?z:i,urlFactory:L}),e,t,g(g({},n),{},{logger:o}))},K=e.createContext({status:`loading`});var q=class{decorateMessage(e){return`[ConfigDirector:nextjs-sdk] ${e}`}};const J=e=>M(e,new q);var Y=class extends t{constructor(e){super(e),this.state={status:`loading`}}buildClient(){var e;return G(this.props.sdkKey,{sdkName:`nextjs-sdk`,sdkVersion:`0.1.0`},{connection:{url:this.props.url,timeout:this.props.timeout},metadata:{appName:this.props.appName,appVersion:this.props.appVersion},logger:(e=this.props.logger)==null?J(`warn`):e})}async componentDidMount(){var e=this;let t=e.buildClient();t.on(`configsUpdated`,()=>{e.setState({updatedAt:new Date})}),t.on(`clientReady`,()=>{e.setState({status:`ready`})}),e.setState({client:t}),await t.initialize(e.props.context),t.isReady||e.setState({status:`default`})}async componentDidUpdate(e){var t=this;if(e.context!==t.props.context){var n;let{client:e}=t.state;if(!e)return;t.setState({status:`loading`}),await e.updateContext((n=t.props.context)==null?{}:n),e.isReady||t.setState({status:`default`})}}componentWillUnmount(){var e;(e=this.state.client)==null||e.dispose()}render(){return r(K.Provider,{value:g(g({},this.state),{},{initialConfigs:this.props.initialConfigs}),children:this.props.children})}},X=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorNextContextError`,Object.setPrototypeOf(this,e.prototype)}};const Z=()=>new X(`The ConfigDirector client was not found in the React context. Make sure the component using this hook is inside a <ConfigDirectorProvider>.`),Q=(e,t)=>{var r,i,a;let o=n(K);if(!o)throw Z();return{value:(r=o.client)!=null&&r.isReady?o.client.getValue(e,t):(i=(a=o.initialConfigs)==null?void 0:a[e])==null?t:i,readyStatus:o.status,loading:o.status===`loading`}},$=()=>{let e=n(K);if(!e)throw Z();return{updateContext:async t=>{var n;await((n=e.client)==null?void 0:n.updateContext(t))}}},ee=()=>{let e=n(K);if(!(e!=null&&e.client))throw Z();return{client:e.client}},te=()=>{let e=n(K);if(!e)throw Z();return{readyStatus:e.status,loading:e.status===`loading`}};export{X as ConfigDirectorNextContextError,Y as ConfigDirectorProvider,J as createConsoleLogger,ee as useClient,te as useConfigDirectorStatus,Q as useConfigValue,$ as useContext};
6
+ `;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}}parseField(e){let t=e.indexOf(`:`);return t===-1?{field:e,value:``}:{field:e.slice(0,t),value:this.extractValue(e,t+1)}}extractValue(e,t){return e[t]===` `?e.slice(t+1):e.slice(t)}};let v=function(e){return e.OPEN=`open`,e.CLOSED=`closed`,e.CONNECTING=`connecting`,e}({});var y=class{constructor(e){var t,n;this.DEFAULT_SHOULD_RECONNECT=()=>!0,this.DEFAULT_CALCULATE_RECONNECT_DELAY=()=>this.serverReconnectionTime,this.reconnectAttempt=0,this.abortController=new AbortController,this._readyState=v.CLOSED,this.serverReconnectionTime=2e3,this.url=e.url.toString(),this.lastEventId=e.lastEventId,this.headers=g({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._fetch=(n=e.fetch)==null?((e,t)=>fetch(e,t)):n,this._onError=this.resolveCallback(e.onError,()=>{},`onError`),this._onConnect=this.resolveCallback(e.onConnect,()=>{},`onConnect`),this._onDisconnect=this.resolveCallback(e.onDisconnect,()=>{},`onDisconnect`),this._onMessage=this.resolveCallback(e.onMessage,()=>{},`onMessage`),this._onComment=this.resolveCallback(e.onComment,()=>{},`onComment`),this._shouldReconnect=this.resolveCallback(e.shouldReconnect,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`),this._calculateReconnectDelay=this.resolveCallback(e.calculateReconnectDelay,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`calculateReconnectDelay`)}set onError(e){this._onError=this.resolveCallback(e,()=>{},`onError`)}set onMessage(e){this._onMessage=this.resolveCallback(e,()=>{},`onMessage`)}set onComment(e){this._onComment=this.resolveCallback(e,()=>{},`onComment`)}set onConnect(e){this._onConnect=this.resolveCallback(e,()=>{},`onConnect`)}set onDisconnect(e){this._onDisconnect=this.resolveCallback(e,()=>{},`onDisconnect`)}set shouldReconnect(e){this._shouldReconnect=this.resolveCallback(e,this.DEFAULT_SHOULD_RECONNECT,`shouldReconnect`)}set calculateReconnectDelay(e){this._calculateReconnectDelay=this.resolveCallback(e,this.DEFAULT_CALCULATE_RECONNECT_DELAY,`shouldReconnect`)}get readyState(){return this._readyState}connect(){this._readyState===v.CLOSED&&(this.reconnectAttempt=0,this.initiateConnection())}close(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this.abortController.abort()}initiateConnection(){this.abortController=new AbortController,this._readyState=v.CONNECTING,this._fetch(this.url,g(g({},this.requestOptions),{},{headers:this.buildRequestHeaders(),signal:this.abortController.signal,cache:`no-store`})).then(e=>this.handleFetchResponse(e)).catch(e=>{if(e.name===`AbortError`||this.abortController.signal.aborted){this.close();return}this._onError(e),this.scheduleReconnect({error: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 c(`The server response did not have a body`);t._readyState=v.OPEN,t._onConnect(),t.reconnectAttempt=0;let i=new _({onEvent:e=>{typeof e.id==`string`&&(t.lastEventId=e.id),t._onMessage(e)},onComment:e=>t._onComment(e),onRetry:e=>t.serverReconnectionTime=e});await t.readResponseStream(n,i),t.scheduleReconnect({status:r,error:new s(`The server response stream was closed`)})}async readResponseStream(e,t){let n=e.getReader(),r=new TextDecoder,i=!1;for(;!i;){let e=await n.read();e.value&&t.parse(r.decode(e.value,{stream:!0})),i=e.done}t.finish()}disconnected(){clearTimeout(this.reconnectTimeout),this._readyState=v.CLOSED,this._onDisconnect()}buildRequestHeaders(){let e=this.lastEventId?{"Last-Event-ID":this.lastEventId}:void 0;return g(g({},this.headers),e)}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.disconnected();return}this._readyState=v.CONNECTING;let n=this._calculateReconnectDelay(t);(!Number.isFinite(n)||n<1||n>1e3*60*60)&&(this._onError(new u(`The calculated reconnect delay is out of range: ${n}. Defaulting to ${this.serverReconnectionTime}`)),n=this.serverReconnectionTime),this.reconnectTimeout=setTimeout(()=>this.initiateConnection(),n)}resolveCallback(e,t,n){if(e==null)return t;if(typeof e!=`function`)throw new l(`${n} must be a function`);return e}};const b=e=>new y(e);var x=class{constructor(e){this.options=e,this.eventEmitter=new i,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/sse/v1`,e.baseUrl)}async connect(e,t){var n=this;n.eventSource&&n.close();let r=new Promise((t,r)=>{n.eventSource=b({url:n.url,method:`POST`,headers:{"Content-Type":`application/json`},fetch:n.options.fetch,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=n.options.connectionRetryDelay(e.attempt);return n.logger.warn(`[EventSourceTransport] Scheduling reconnect in ${t}ms.`),t},onError:e=>{n.logger.debug(`[EventSourceTransport] Error: `,e)},onDisconnect:()=>{n.logger.debug(`[EventSourceTransport] Disconnected`)}}),n.eventSource.connect()});return clearTimeout(n.timeoutTimer),n.timeoutTimer=void 0,Promise.race([r,new Promise(e=>{n.timeoutTimer=setTimeout(()=>e(n),t)})]).finally(()=>clearTimeout(n.timeoutTimer))}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 n=e==null?0:e;return new a(`${`Connection failed with status: ${e==null?`unknown`:e}.`}${t?` Error: ${t}`:``}. This is an unrecoverable error, will not attempt to reconnect.`,n)}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(),clearTimeout(this.timeoutTimer)}dispose(){this.close(),this.clear()}};const S=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},C=e=>e===`number`||e===`bigint`,w=(e,t)=>{let n=e.value,r=S(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=T(n),i=typeof e==`boolean`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-boolean`}}if(C(typeof t)&&e.type===`integer`){let e=E(n),i=typeof e==`number`;return{parsedValue:i?e:t,requestedType:r,usedDefault:!i,reason:i?`found-match`:`invalid-number`}}if(C(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`}},T=e=>{if(!e)return;let t=e.toLowerCase();if(!(t!=`true`&&t!=`false`))return t===`true`},E=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},O={off:-1,error:0,warn:1,info:2,debug:3},k=()=>{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,g(g({},e),{},{fractionalSecondDigits:3}))}catch(t){return new Intl.DateTimeFormat(void 0,e)}};var A=class{constructor(e,t){this.level=e,this.decorator=t,this.level=e,this.decorator=t,this.dateFormatter=k()}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(O[this.level]>=O[t]){var i;e(`[${this.dateFormatter.format(new Date)}] ${(i=this.decorator)==null?void 0:i.decorateMessage(n)}`,...r)}}},j=class{decorateMessage(e){return`[ConfigDirector:js-client-sdk] ${e}`}};const M=(e,t)=>new A(e==null?`warn`:e,t==null?new j:t);var N=class{constructor(){this.fatalError=!1,this.pollingIntervalSeconds=60}clearPollingInterval(){clearInterval(this.pollingInterval)}schedulePollingInterval(e){clearInterval(this.pollingInterval),!(this.pollingIntervalSeconds<=0)&&(this.pollingInterval=setInterval(e,this.pollingIntervalSeconds*1e3))}async handleNonOkResponse(e){var t=this;if(!e.ok)throw t.isStatusFatal(e.status)?(t.fatalError=!0,t.prepareFatalResponseStatusError(e.status,await e.text())):new a(`Connection failed with status: ${e.status}`,e.status)}handleFetchError(e){throw this.isFetchErrorFatal(e)?(this.fatalError=!0,this.close(),new a(`Connection failed with fatal error: ${e}. This is an unrecoverable error, retry attempts will be ignored.`)):e instanceof SyntaxError?new a(`Failed to parse the response from the server: ${e}`):new a(`Connection failed with error: ${e}.`)}prepareFatalResponseStatusError(e,t){var n,r;let i=e==null?0:e;return new a(`${`Connection failed with status: ${e==null?`unknown`:e}`}${((n=t==null||(r=t.trim())==null?void 0:r.length)==null?0:n)>0?` (${t})`:``}. This is an unrecoverable error, retry attempts will be ignored.`,i)}isStatusFatal(e){return!!e&&e>=400&&e<500}isFetchErrorFatal(e){return(e==null?void 0:e.name)===`NotAllowedError`?!0:e instanceof TypeError}get isConnected(){return this.pollingInterval!==void 0}close(){clearInterval(this.pollingInterval),this.pollingInterval=void 0}dispose(){this.close(),this.clear()}};const P=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(typeof t==`string`||t instanceof Request?t:t.toString(),g(g({},n),{},{signal:i.signal}));return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}};var F=class extends N{constructor(e){var t;super(),this.options=e,this.eventEmitter=new i,this.options=e,this.logger=e.logger,this.url=e.resolveUrl(`client/polling/v1`,e.baseUrl),this.pollingIntervalSeconds=(t=e.pollingInterval)==null?60:t}async connect(e,t){var n=this;return n.clearPollingInterval(),await n.fetchConfigs(e,t),n.schedulePollingInterval(()=>{n.fetchConfigs(e,t).catch(e=>{n.logger.error(`[PollingTransport] Error during polling:`,e)})}),n}async fetchConfigs(e,t){var n=this;if(n.fatalError){n.logger.warn(`[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.`);return}try{let r=await P(t,n.url,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({givenContext:e,metaContext:n.options.metaContext,clientSdkKey:n.options.clientSdkKey,lastUpdateTimestamp:n.lastUpdateTimestamp})},n.logger);if(await n.handleNonOkResponse(r),r.status==200){let e=JSON.parse(await r.text());n.lastUpdateTimestamp=e.timestamp,n.eventEmitter.emit(`configSetReceived`,e)}}catch(e){n.handleFetchError(e)}}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}clear(){this.eventEmitter.clear()}},I=class extends F{constructor(e){super(e),this.pollingIntervalSeconds=0}};const L=(e,t)=>new URL(e,t==null?void 0:t.toString()),R=e=>{if(e)try{return new URL(e)}catch(t){throw new o(`Invalid URL '${e}'. Parsing failed: ${t}`)}},z=`https://client-sdk-api.configdirector.com`;var B=class{constructor(e,t,n,r,a){var o,s,c,l,u,d,f,p,m,h,_;this.handlersMap=new Map,this.eventEmitter=new i,this.ready=!1,this.initializing=!1,this.logger=(o=r==null?void 0:r.logger)==null?M():o,this.timeout=(s=r==null||(c=r.connection)==null?void 0:c.timeout)==null?3e3:s;let v=(l=a==null?void 0:a.urlFactory)==null?L:l,y=(u=this.parseUrl(r==null||(d=r.connection)==null?void 0:d.url,v))==null?z:u;this.connectionMode=(f=r==null||(p=r.connection)==null?void 0:p.mode)==null?`streaming`:f;let b=this.getTransportConstructor(this.connectionMode);this.telemetryClient=e,this.transport=new b({clientSdkKey:t,baseUrl:y,resolveUrl:v,metaContext:g(g({},r==null?void 0:r.metadata),{},{sdkName:n.sdkName,sdkVersion:n.sdkVersion,userAgent:(m=navigator)==null?void 0:m.userAgent}),logger:this.logger,fetch:a==null?void 0:a.fetch,connectionRetryDelay:(h=a==null?void 0:a.connectionRetryDelay)==null?(e=>2**Math.min(e,9)*1e3):h,pollingInterval:r==null||(_=r.connection)==null?void 0:_.pollingInterval}),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=g(g({},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})})}getTransportConstructor(e){switch(e){case`one-time`:return I;case`polling`:return F;default:return x}}async initialize(e){var t=this;t.initializing=!0,await t.connectToTransport(e,`initialization`)}async updateContext(e){await this.connectToTransport(e,`context update`)}get context(){return this.currentContext}async connectToTransport(e,t){var n=this;try{n.ready=!1,n.readyPromise=new Promise(e=>{n.readyResolve=e}).then(()=>{n.ready=!0,n.initializing=!1,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.telemetryClient.updateContext(e),n.eventEmitter.emit(`contextUpdated`,{context:e});let i=new Date().getTime()-r,a=n.timeout-i;clearTimeout(n.timeoutTimer),n.timeoutTimer=void 0,a>0&&await Promise.race([n.readyPromise,new Promise(e=>{n.timeoutTimer=setTimeout(()=>e(),a)})]).finally(()=>clearTimeout(n.timeoutTimer)),n.ready||n.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${t} after ${n.timeout}ms. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`)}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.telemetryClient.evaluatedConfig({contextId:(i=this.currentContext)==null?void 0:i.id,key:e,defaultValue:n,requestedType:S(n),evaluatedValue:n,usedDefault:!0,evaluationReason:`config-state-missing`}),n}let a=w(t,n);return this.telemetryClient.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,t){if(e)try{return t(e)}catch(t){throw new o(`Invalid base URL '${e}'. Parsing failed: ${t}`)}}validateDefaultValue(e){if(e==null)throw new o(`Invalid default value. The default value for a config must be defined and non-null.`);if(typeof e==`function`)throw new o(`Invalid default value. The default value for a config cannot be a function.`)}get isReady(){return this.ready}get isInitializing(){return this.initializing}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()}pauseNetwork(){this.logger.debug(`[ConfigDirectorClient] pauseNetwork() called, pausing transport connection`),clearTimeout(this.timeoutTimer),this.timeoutTimer=void 0,this.transport.close(),this.ready=!1}async resumeNetwork(){var e=this;await e.connectToTransport(e.currentContext,`network resume`)}async close(){var e=this,t;e.logger.debug(`[ConfigDirectorClient] close() has been called, closing connection to server`),clearTimeout(e.timeoutTimer),(t=e.readyResolve)==null||t.call(e),e.telemetryClient.close(),e.transport.close(),e.ready=!1}dispose(){this.clear(),this.close()}};const V=(e,t)=>{if(t===`json`)try{return H(JSON.stringify(e))}catch(t){return e.toString().slice(0,500)}return e.toString().slice(0,500)},H=e=>U(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),U=e=>{let t=5381;for(let r=0;r<e.length;r++){var n;t=(t<<5)+t+((n=e[r])==null?0:n),t>>>=0}return t};var W=class{constructor(e){var t,n,r;this.logger=e.logger;let i={type:`Initialize`,payload:{sdkKey:e.sdkKey,baseUrl:e.baseUrl.toString(),evaluationQueueLimit:(t=e.evaluationQueueLimit)==null?1e3:t,initialFlushIntervalDelay:(n=e.initialFlushIntervalDelay)==null?5e3:n,flushIntervalDelay:(r=e.flushIntervalDelay)==null?3e4:r}};this.worker=new Worker(URL.createObjectURL(new Blob(["(function(){let e=(e,n)=>{if(n===`json`)try{return t(JSON.stringify(e))}catch{return e.toString().slice(0,500)}return e.toString().slice(0,500)},t=e=>n(new TextEncoder().encode(e)).toString(16).padStart(8,`0`),n=e=>{let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+(e[n]??0),t>>>=0;return t},r=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},i=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},a=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(typeof t==`string`||t instanceof Request?t:t.toString(),{...n,signal:i.signal});return clearTimeout(a),e}catch(e){throw r.warn(`[fetchWithTimeout] Fetch error: `,e),clearTimeout(a),e}},o=e=>e?.name===`NotAllowedError`?!0:e instanceof TypeError;var s=class{constructor(e){this.executeRequests=!0,this.sdkKey=e.sdkKey,this.logger=e.logger,this.url=e.urlFactory(`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=await this.sendReport(i);return a.fatalError&&(this.executeRequests=!1),a}isReportEmpty(e){return r(e.discreteEvents)&&r(e.aggregatedEvents)&&i(e.droppedEvents)}async sendReport(e){try{let t=await a(5e3,this.url,{method:`POST`,body:JSON.stringify(e),keepalive:!0},this.logger),n=this.isStatusFatal(t.status);return n&&this.logger.warn(`[EventReporter] Received a fatal status response (${t.status}) from the telemetry endpoint. No more telemetry data will be sent.`),{success:t.ok,fatalError:n}}catch(e){let t=o(e);return this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${e}`,{error:e}),{success:!1,fatalError:t}}}isStatusFatal(e){return!!e&&e>=400&&e<500}},c=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}))}},l=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}},u=class{constructor(e){this.aggregator=new c,this.collectEvents=!0,this.logger=e.logger,this.evaluationEventQueue=new l(e.evaluationQueueLimit??1e3),this.flushIntervalDelay=e.flushIntervalDelay??3e4;let t=e.initialFlushIntervalDelay??5e3;this.flushTimeout=setTimeout(()=>this.flushAndScheduleNext(),t)}sanitizeValue(t,n){return e(t,n)}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)}async close(){this.collectEvents=!1,clearTimeout(this.flushTimeout),await this.flush(),this.evaluationEventQueue.clear()}dispose(){this.close()}},d=class extends u{constructor(e){super(e),this.reporter=new s(e)}async updateContext(e){this.cancelScheduledFlush(),await this.flush(),this._context=e,await this.flushAndScheduleNext()}evaluatedConfig(e){this.collectEvents&&this.evaluationEventQueue.push(e)}async forceFlush(){this.cancelScheduledFlush(),await this.flushAndScheduleNext()}};let f=(e,t)=>new URL(e,t?.toString()),p,m=(e,t,...n)=>({type:`Log`,payload:{level:e,message:t,args:n}}),h=e=>{try{self.postMessage(e)}catch{self.postMessage(m(e.payload.level,e.payload.message))}},g={debug:function(e,...t){h(m(`debug`,e,t))},info:function(e,...t){h(m(`info`,e,t))},warn:function(e,...t){h(m(`warn`,e,t))},error:function(e,...t){h(m(`error`,e,t))}};addEventListener(`message`,e=>{let t=e.data;if(t.type!=`Initialize`&&!p){g.info(`[TelemetryWorker] Ignoring ${t.type} because no collector is configured`);return}switch(g.debug(`[TelemetryWorker] Received '${t.type}' event:`,t),t.type){case`Initialize`:p=new d({...t.payload,baseUrl:f(t.payload.baseUrl),urlFactory:f,logger:g});break;case`EvaluatedConfigEvent`:p?.evaluatedConfig(t.payload);break;case`UpdateContext`:p?.updateContext(t.payload.context);break;case`Flush`:p?.forceFlush();break;case`Close`:(p?.close()??Promise.resolve()).then(()=>{self.postMessage({type:`Closed`}),self.close()});break}})})();"],{type:`application/javascript`}))),this.worker.onmessage=e=>{this.handleWorkerEvent(e.data)},this.worker.postMessage(i),this.visibilityHandler=()=>{document.visibilityState===`hidden`&&this.flush()};try{document.addEventListener(`visibilitychange`,this.visibilityHandler)}catch(e){this.logger.warn(`[TelemetryEventCollector] Could not configure 'visibilitychange' listener: `,e)}}async updateContext(e){var t=this;let n={type:`UpdateContext`,payload:{context:e}};t.worker.postMessage(n)}evaluatedConfig(e){let t={type:`EvaluatedConfigEvent`,payload:this.sanitizeEvaluatedConfigEvent(e)};this.worker.postMessage(t)}sanitizeEvaluatedConfigEvent(e){return g(g({},e),{},{defaultValue:V(e.defaultValue,e.type),evaluatedValue:V(e.evaluatedValue,e.type)})}handleWorkerEvent(e){var t,n;if(e.type===`Closed`){var r;(r=this.closeResolve)==null||r.call(this);return}if(!((t=e.payload)!=null&&t.level)||!((n=e.payload)!=null&&n.message))return;let{level:i,message:a,args:o}=e.payload;switch(i){case`debug`:this.logger.debug(a,...o);break;case`info`:this.logger.info(a,...o);break;case`warn`:this.logger.warn(a,...o);break;case`error`:this.logger.warn(a,...o);break}}flush(){this.worker.postMessage({type:`Flush`})}close(){return this.closePromise?this.closePromise:this.performClose()}performClose(){return document.removeEventListener(`visibilitychange`,this.visibilityHandler),this.closePromise=new Promise(e=>{this.closeResolve=e,this.worker.postMessage({type:`Close`})}),this.closePromise}};const G=(e,t,n)=>{var r,i,a;let o=(r=n==null?void 0:n.logger)==null?M():r;return new B(new W({sdkKey:e,logger:o,baseUrl:(i=R(n==null||(a=n.connection)==null?void 0:a.url))==null?z:i,urlFactory:L}),e,t,g(g({},n),{},{logger:o}))},K=e.createContext({status:`loading`});var q=class{decorateMessage(e){return`[ConfigDirector:nextjs-sdk] ${e}`}};const J=e=>M(e,new q);var Y=class extends t{constructor(e){super(e),this.state={status:`loading`}}buildClient(){var e;return G(this.props.sdkKey,{sdkName:`nextjs-sdk`,sdkVersion:`0.2.0`},{connection:{url:this.props.url,timeout:this.props.timeout},metadata:{appName:this.props.appName,appVersion:this.props.appVersion},logger:J((e=this.props.logLevel)==null?`warn`:e)})}async componentDidMount(){var e=this;let t=e.buildClient();t.on(`configsUpdated`,()=>{e.setState({updatedAt:new Date})}),t.on(`clientReady`,()=>{e.setState({status:`ready`})}),e.setState({client:t}),await t.initialize(e.props.context),t.isReady||e.setState({status:`default`})}async componentDidUpdate(e){var t=this;if(e.context!==t.props.context){var n;let{client:e}=t.state;if(!e)return;t.setState({status:`loading`}),await e.updateContext((n=t.props.context)==null?{}:n),e.isReady||t.setState({status:`default`})}}componentWillUnmount(){var e;(e=this.state.client)==null||e.dispose()}render(){return r(K.Provider,{value:g(g({},this.state),{},{initialConfigs:this.props.initialConfigs}),children:this.props.children})}},X=class e extends Error{constructor(t){super(t),this.name=`ConfigDirectorNextContextError`,Object.setPrototypeOf(this,e.prototype)}};const Z=()=>new X(`The ConfigDirector client was not found in the React context. Make sure the component using this hook is inside a <ConfigDirectorProvider>.`),Q=(e,t)=>{var r,i;let a=n(K);if(!a)throw Z();let o=(r=a.initialConfigs)==null?void 0:r[e];return{value:(i=a.client)!=null&&i.isReady?a.client.getValue(e,t):o?w(o,t).parsedValue:t,readyStatus:a.status,loading:a.status===`loading`}},$=()=>{let e=n(K);if(!e)throw Z();return{updateContext:async t=>{var n;await((n=e.client)==null?void 0:n.updateContext(t))}}},ee=()=>{let e=n(K);if(!(e!=null&&e.client))throw Z();return{client:e.client}},te=()=>{let e=n(K);if(!e)throw Z();return{readyStatus:e.status,loading:e.status===`loading`}};export{X as ConfigDirectorNextContextError,Y as ConfigDirectorProvider,J as createConsoleLogger,ee as useClient,te as useConfigDirectorStatus,Q as useConfigValue,$ as useContext};
7
7
  //# sourceMappingURL=index.mjs.map