@configdirector/server-sdk 1.9.1 → 1.11.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultConfigDirectorClient-z8HbdUmy.mjs","names":["EventEmitter","EventEmitter","isDecimalCharacters"],"sources":["../../shared/src/errors.ts","../../eventsource/src/errors.ts","../../eventsource/src/EventSourceParser.ts","../../eventsource/src/types.ts","../../eventsource/src/EventSourceClient.ts","../../eventsource/src/index.ts","../../shared/src/fetchWithTimeout.ts","../src/StreamingTransport.ts","../../shared/src/transport/AbstractPollingTransport.ts","../src/PollingTransport.ts","../../shared/src/native-values.ts","../src/native-values.ts","../src/OfflineTransport.ts","../src/connection-plan.ts","../../config-evaluator/src/json-pointer.ts","../../config-evaluator/src/text-comparison.ts","../../config-evaluator/src/numeric-comparison.ts","../../config-evaluator/src/date-comparison.ts","../../config-evaluator/src/semver-comparison.ts","../../config-evaluator/src/render.ts","../../config-evaluator/src/array-comparison.ts","../../config-evaluator/src/context-kind.ts","../../config-evaluator/src/ConditionEvaluator.ts","../../config-evaluator/src/SegmentEvaluator.ts","../../config-evaluator/src/percent-hashing.ts","../../config-evaluator/src/ConfigEvaluator.ts","../../shared/src/value-parser.ts","../../shared/src/logger.ts","../src/logger.ts","../../shared/src/url.ts","../../shared/src/transport/pollingInterval.ts","../../shared/src/context-kind.ts","../../shared/src/telemetry/InvalidContextSubmissions.ts","../../shared/src/telemetry/utils.ts","../src/telemetry/ServerEventReporter.ts","../../shared/src/telemetry/EventAggregator.ts","../../shared/src/telemetry/EventQueue.ts","../../shared/src/telemetry/TelemetryEventCollector.ts","../../shared/src/LimitedMap.ts","../../shared/src/telemetry/EvaluatedConfigEventPreprocessor.ts","../src/telemetry/ServerTelemetryEventCollector.ts","../src/telemetry/discarding.ts","../../shared/src/base62-encoder.ts","../../shared/src/value-id.ts","../src/telemetry/value-id-generator.ts","../src/DefaultConfigDirectorClient.ts"],"sourcesContent":["export class ConfigDirectorConnectionError extends Error {\n public override readonly name: string = \"ConfigDirectorConnectionError\";\n public readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.status = status;\n\n Object.setPrototypeOf(this, ConfigDirectorConnectionError.prototype);\n }\n}\n\nexport class ConfigDirectorValidationError extends Error {\n public override readonly name: string = \"ConfigDirectorValidationError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ConfigDirectorValidationError.prototype);\n }\n}\n\nexport class ConfigDirectorInitializationError extends Error {\n public override readonly name: string = \"ConfigDirectorInitializationError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ConfigDirectorInitializationError.prototype);\n }\n}\n","export class StreamClosedError extends Error {\n public override readonly name: string = \"StreamClosedError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, StreamClosedError.prototype);\n }\n}\n\nexport class MissingResponseBodyError extends Error {\n public override readonly name: string = \"MissingResponseBodyError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, MissingResponseBodyError.prototype);\n }\n}\n\nexport class InvalidOptionError extends Error {\n public override readonly name: string = \"InvalidOptionError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, InvalidOptionError.prototype);\n }\n}\n\nexport class ValueOutOfRangeError extends Error {\n public override readonly name: string = \"ValueOutOfRangeError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ValueOutOfRangeError.prototype);\n }\n}\n","import {\n type EventSourceParserOptions,\n type EventSourceMessageHandler,\n type EventSourceCommentHandler,\n type EventParserRetryCallback,\n} from \"./types\";\n\ntype ParsedField = {\n field: string;\n value: string;\n};\n\nexport class EventSourceParser {\n private isFirstChunk = true;\n private bufferedInput = \"\";\n private currentEvent: { type?: string; data: string } = { data: \"\" };\n private lastEventId: string | undefined;\n private onEvent: EventSourceMessageHandler;\n private onRetry: EventParserRetryCallback | undefined;\n private onComment: EventSourceCommentHandler | undefined;\n\n constructor(options: EventSourceParserOptions) {\n this.currentEvent = { data: \"\" };\n this.onEvent = options.onEvent ?? (() => {});\n this.onRetry = options.onRetry;\n this.onComment = options.onComment;\n }\n\n public parse(chunk: string): void {\n let input = chunk;\n\n if (this.isFirstChunk) {\n this.isFirstChunk = false;\n if (input.startsWith(\"\\uFEFF\")) {\n input = input.slice(1);\n } else if (input.startsWith(\"\\xEF\\xBB\\xBF\")) {\n input = input.slice(3);\n }\n }\n\n const lines = this.scanLines(this.bufferedInput + input);\n for (const line of lines) {\n this.dispatchLine(line);\n }\n }\n\n // Per the SSE spec, any data still buffered when the stream ends is discarded —\n // an event requires a terminating empty line to be dispatched.\n public finish(): void {\n this.bufferedInput = \"\";\n }\n\n /**\n * Scans `text` character by character to extract complete lines, recognizing\n * CR, LF, and CRLF as line terminators per the SSE spec. Any unterminated\n * trailing text is buffered and prepended to the next chunk.\n */\n private scanLines(text: string): string[] {\n const lines: string[] = [];\n let lineStart = 0;\n let i = 0;\n\n while (i < text.length) {\n const ch = text[i];\n if (ch === \"\\r\" || ch === \"\\n\") {\n lines.push(text.slice(lineStart, i));\n // Consume CRLF as a single terminator rather than two separate lines.\n if (ch === \"\\r\" && text[i + 1] === \"\\n\") {\n i++;\n }\n i++;\n lineStart = i;\n } else {\n i++;\n }\n }\n\n this.bufferedInput = text.slice(lineStart);\n return lines;\n }\n\n private dispatchLine(line: string): void {\n if (line.startsWith(\":\")) {\n this.onComment?.(this.extractValue(line, 1));\n return;\n }\n\n if (line === \"\") {\n this.emitEvent();\n return;\n }\n\n this.applyField(this.parseField(line));\n }\n\n private emitEvent(): void {\n const data = this.currentEvent.data.endsWith(\"\\n\")\n ? this.currentEvent.data.slice(0, -1)\n : this.currentEvent.data;\n\n if (data !== \"\") {\n this.onEvent({ ...this.currentEvent, id: this.lastEventId, data });\n }\n\n this.currentEvent = { data: \"\" };\n }\n\n private applyField(parsed: ParsedField): void {\n switch (parsed.field) {\n case \"event\":\n this.currentEvent.type = parsed.value;\n break;\n case \"data\":\n this.currentEvent.data += parsed.value + \"\\n\";\n break;\n case \"id\":\n // Spec: ignore id values that contain a null character.\n if (!parsed.value.includes(\"\\0\")) {\n this.lastEventId = parsed.value;\n }\n break;\n case \"retry\":\n if (/^\\d+$/.test(parsed.value)) {\n this.onRetry?.(Number.parseInt(parsed.value, 10));\n }\n break;\n default:\n break;\n }\n }\n\n private parseField(line: string): ParsedField {\n const colon = line.indexOf(\":\");\n if (colon === -1) {\n return { field: line, value: \"\" };\n }\n return { field: line.slice(0, colon), value: this.extractValue(line, colon + 1) };\n }\n\n /** Returns the value portion of a line, stripping a single leading space per spec. */\n private extractValue(line: string, from: number): string {\n return line[from] === \" \" ? line.slice(from + 1) : line.slice(from);\n }\n}\n","export type ReconnectionState = {\n attempt: number;\n serverReconnectionTime: number;\n status?: number | undefined;\n error?: Error | undefined;\n};\n\nexport enum ReadyState {\n OPEN = \"open\",\n CLOSED = \"closed\",\n CONNECTING = \"connecting\",\n}\n\nexport type EventSourceClientOptions = {\n url: { toString(): string } | string;\n method?: string;\n headers?: Record<string, string>;\n body?: string | (() => string);\n mode?: \"cors\" | \"no-cors\" | \"same-origin\";\n credentials?: \"include\" | \"omit\" | \"same-origin\";\n redirect?: \"error\" | \"follow\";\n referrer?: string;\n referrerPolicy?: ReferrerPolicy;\n lastEventId?: string;\n fetch?: (url: string, init: RequestInit) => Promise<Response>;\n onError?: (error: Error) => void;\n onConnect?: () => void;\n onDisconnect?: () => void;\n onMessage?: EventSourceMessageHandler;\n onComment?: EventSourceCommentHandler;\n shouldReconnect?: (state: ReconnectionState) => boolean;\n calculateReconnectDelay?: (state: ReconnectionState) => number;\n};\n\nexport type EventSourceMessage = {\n id?: string | undefined;\n type?: string;\n data: string;\n};\n\nexport type EventSourceMessageHandler = (message: EventSourceMessage) => void;\nexport type EventSourceCommentHandler = (comment: string) => void;\n\nexport type EventParserRetryCallback = (retry: number) => void;\n\nexport type EventSourceParserOptions = {\n onEvent?: EventSourceMessageHandler;\n onRetry?: EventParserRetryCallback;\n onComment?: EventSourceCommentHandler;\n};\n","import {\n InvalidOptionError,\n MissingResponseBodyError,\n StreamClosedError,\n ValueOutOfRangeError,\n} from \"./errors\";\nimport { EventSourceParser } from \"./EventSourceParser\";\nimport {\n type EventSourceClientOptions,\n type EventSourceCommentHandler,\n type EventSourceMessageHandler,\n type ReconnectionState,\n ReadyState,\n} from \"./types\";\n\nexport class EventSourceClient {\n private readonly DEFAULT_SHOULD_RECONNECT = () => true;\n private readonly DEFAULT_CALCULATE_RECONNECT_DELAY = () => this.serverReconnectionTime;\n private readonly url: string;\n private readonly headers?: Record<string, string>;\n private readonly body: () => string | undefined;\n private readonly requestOptions;\n private lastEventId?: string;\n private reconnectAttempt: number = 0;\n private abortController: AbortController = new AbortController();\n private reconnectTimeout: ReturnType<typeof setTimeout> | undefined;\n private _readyState: ReadyState = ReadyState.CLOSED;\n private serverReconnectionTime: number = 2_000;\n private _fetch: (url: string, init: RequestInit) => Promise<Response>;\n private _onError: (error: Error) => void;\n private _onMessage: EventSourceMessageHandler;\n private _onComment: EventSourceCommentHandler;\n private _onConnect: () => void;\n private _onDisconnect: () => void;\n private _shouldReconnect: (state: ReconnectionState) => boolean;\n private _calculateReconnectDelay: (state: ReconnectionState) => number;\n\n constructor(options: EventSourceClientOptions) {\n this.url = options.url.toString();\n this.lastEventId = options.lastEventId;\n this.headers = {\n Accept: \"text/event-stream\",\n ...options.headers,\n };\n const body = options.body;\n this.body = typeof body === \"function\" ? body : () => body;\n this.requestOptions = {\n method: options.method ?? \"GET\",\n mode: options.mode,\n credentials: options.credentials,\n redirect: options.redirect,\n referrer: options.referrer,\n referrerPolicy: options.referrerPolicy,\n };\n this._fetch = options.fetch ?? ((url, init) => fetch(url, init));\n this._onError = this.resolveCallback(options.onError, () => {}, \"onError\");\n this._onConnect = this.resolveCallback(options.onConnect, () => {}, \"onConnect\");\n this._onDisconnect = this.resolveCallback(options.onDisconnect, () => {}, \"onDisconnect\");\n this._onMessage = this.resolveCallback(options.onMessage, () => {}, \"onMessage\");\n this._onComment = this.resolveCallback(options.onComment, () => {}, \"onComment\");\n this._shouldReconnect = this.resolveCallback(\n options.shouldReconnect,\n this.DEFAULT_SHOULD_RECONNECT,\n \"shouldReconnect\",\n );\n this._calculateReconnectDelay = this.resolveCallback(\n options.calculateReconnectDelay,\n this.DEFAULT_CALCULATE_RECONNECT_DELAY,\n \"calculateReconnectDelay\",\n );\n }\n\n public set onError(value: (error: Error) => void) {\n this._onError = this.resolveCallback(value, () => {}, \"onError\");\n }\n\n public set onMessage(value: EventSourceMessageHandler) {\n this._onMessage = this.resolveCallback(value, () => {}, \"onMessage\");\n }\n\n public set onComment(value: EventSourceCommentHandler) {\n this._onComment = this.resolveCallback(value, () => {}, \"onComment\");\n }\n\n public set onConnect(value: () => void) {\n this._onConnect = this.resolveCallback(value, () => {}, \"onConnect\");\n }\n\n public set onDisconnect(value: () => void) {\n this._onDisconnect = this.resolveCallback(value, () => {}, \"onDisconnect\");\n }\n\n public set shouldReconnect(value: (state: ReconnectionState) => boolean) {\n this._shouldReconnect = this.resolveCallback(value, this.DEFAULT_SHOULD_RECONNECT, \"shouldReconnect\");\n }\n\n public set calculateReconnectDelay(value: (state: ReconnectionState) => number) {\n this._calculateReconnectDelay = this.resolveCallback(\n value,\n this.DEFAULT_CALCULATE_RECONNECT_DELAY,\n \"shouldReconnect\",\n );\n }\n\n public get readyState(): ReadyState {\n return this._readyState;\n }\n\n public connect() {\n if (this._readyState !== ReadyState.CLOSED) {\n return;\n }\n\n this.reconnectAttempt = 0;\n this.initiateConnection();\n }\n\n public close() {\n clearTimeout(this.reconnectTimeout);\n this._readyState = ReadyState.CLOSED;\n this.abortController.abort();\n }\n\n private initiateConnection() {\n this.abortController = new AbortController();\n this._readyState = ReadyState.CONNECTING;\n\n this._fetch(this.url, {\n ...this.requestOptions,\n body: this.body(),\n headers: this.buildRequestHeaders(),\n signal: this.abortController.signal,\n cache: \"no-store\",\n })\n .then((response) => this.handleFetchResponse(response))\n .catch((error: Error) => {\n if (error.name === \"AbortError\" || this.abortController.signal.aborted) {\n this.close();\n return;\n }\n\n this._onError(error);\n this.scheduleReconnect({ error });\n });\n }\n\n private async handleFetchResponse(response: Response) {\n const { body, status } = response;\n\n if (status === 204) {\n this.disconnected();\n return;\n }\n\n if (status >= 400) {\n this.scheduleReconnect({ status });\n return;\n }\n\n if (!body) {\n throw new MissingResponseBodyError(\"The server response did not have a body\");\n }\n\n this._readyState = ReadyState.OPEN;\n this._onConnect();\n this.reconnectAttempt = 0;\n\n const parser = new EventSourceParser({\n onEvent: (event) => {\n if (typeof event.id === \"string\") {\n this.lastEventId = event.id;\n }\n this._onMessage(event);\n },\n onComment: (comment) => this._onComment(comment),\n onRetry: (retryDelay) => (this.serverReconnectionTime = retryDelay),\n });\n\n await this.readResponseStream(body, parser);\n\n this.scheduleReconnect({\n status,\n error: new StreamClosedError(\"The server response stream was closed\"),\n });\n }\n\n private async readResponseStream(body: ReadableStream<Uint8Array>, parser: EventSourceParser) {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n if (result.value) {\n parser.parse(decoder.decode(result.value, { stream: true }));\n }\n done = result.done;\n }\n\n parser.finish();\n }\n\n private disconnected() {\n clearTimeout(this.reconnectTimeout);\n this._readyState = ReadyState.CLOSED;\n this._onDisconnect();\n }\n\n private buildRequestHeaders(): Record<string, string> {\n const lastEventIdHeader = this.lastEventId ? { \"Last-Event-ID\": this.lastEventId } : undefined;\n return {\n ...this.headers,\n ...lastEventIdHeader,\n };\n }\n\n private scheduleReconnect(state: { status?: number; error?: Error }) {\n if (this.abortController.signal.aborted) {\n return;\n }\n\n clearTimeout(this.reconnectTimeout);\n this.reconnectAttempt += 1;\n\n const reconnectionState: ReconnectionState = {\n attempt: this.reconnectAttempt,\n status: state.status,\n error: state.error,\n serverReconnectionTime: this.serverReconnectionTime,\n };\n\n if (!this._shouldReconnect(reconnectionState)) {\n this.disconnected();\n return;\n }\n\n this._readyState = ReadyState.CONNECTING;\n let delay = this._calculateReconnectDelay(reconnectionState);\n\n if (!Number.isFinite(delay) || delay < 1 || delay > 1_000 * 60 * 60) {\n this._onError(\n new ValueOutOfRangeError(\n `The calculated reconnect delay is out of range: ${delay}. Defaulting to ${this.serverReconnectionTime}`,\n ),\n );\n delay = this.serverReconnectionTime;\n }\n\n this.reconnectTimeout = setTimeout(() => this.initiateConnection(), delay);\n }\n\n private resolveCallback<T extends (...args: any[]) => any>(\n provided: unknown,\n fallback: T,\n optionName: string,\n ): T {\n if (provided == null) return fallback;\n if (typeof provided !== \"function\") {\n throw new InvalidOptionError(`${optionName} must be a function`);\n }\n return provided as T;\n }\n}\n","import { EventSourceClient } from \"./EventSourceClient\";\nimport { type EventSourceClientOptions } from \"./types\";\n\nexport const createEventSourceClient = (options: EventSourceClientOptions) => {\n return new EventSourceClient(options);\n};\n","import { type ConfigDirectorLogger } from \"./types\";\nimport type { UrlLike } from \"./url\";\n\nexport const fetchWithTimeout = async (\n timeout: number,\n resource: string | UrlLike | Request,\n options: RequestInit | undefined,\n logger: ConfigDirectorLogger,\n) => {\n const abortController = new AbortController();\n const abortTimeoutId = setTimeout(() => {\n logger.debug(\"[fetchWithTimeout] Reached timeout, aborting request\");\n abortController.abort();\n }, timeout);\n\n try {\n const response = await fetch(typeof resource === \"string\" || resource instanceof Request ? resource : resource.toString(), {\n ...options,\n signal: abortController.signal,\n });\n clearTimeout(abortTimeoutId);\n return response;\n } catch (error) {\n logger.warn(\"[fetchWithTimeout] Fetch error: \", error);\n clearTimeout(abortTimeoutId);\n throw error;\n }\n};\n","import type {\n ConfigBundle,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { ConfigDirectorConnectionError } from \"@shared/errors\";\nimport { EventEmitter } from \"node:events\";\nimport { randomUUID } from \"node:crypto\";\nimport { createEventSourceClient } from \"@eventsource/index\";\nimport type { EventSourceClient } from \"@eventsource/EventSourceClient\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\n\nconst MAX_EXPONENTIAL_DELAY = 9; // 2^9 = 512 seconds, to cap it to under 10min\n\nconst HEARTBEAT_INTERVAL_MS = 90_000;\n\nexport class StreamingTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventSource: EventSourceClient | undefined;\n private eventEmitter = new EventEmitter();\n private url: URL;\n private heartbeatUrl: URL;\n private heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n private _sessionId: string | undefined;\n\n constructor(private readonly options: TransportOptions) {\n this.options = options;\n this.logger = options.logger;\n this.url = new URL(\"server/sse/v1\", options.baseUrl);\n this.heartbeatUrl = new URL(\"server/heartbeat/v1\", options.baseUrl);\n }\n\n public async connect(timeout: number): Promise<this> {\n if (this.eventSource) {\n this.close();\n }\n\n const eventSourcePromise = new Promise<this>((resolve, reject) => {\n this.eventSource = createEventSourceClient({\n url: this.url,\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: () => this.buildRequestBody(),\n\n onMessage: ({ data }) => {\n this.dispatchMessage(data);\n },\n onConnect: () => {\n this.logger.debug(\"[StreamingTransport] Connected\");\n resolve(this);\n },\n shouldReconnect: (state) => {\n const reconnect = !this.isStatusFatal(state.status);\n if (!reconnect) {\n this.stopHeartbeat();\n const error = this.prepareFatalError(state.status, state.error);\n this.eventEmitter.emit(\"connectionError\", error);\n reject(error);\n }\n return reconnect;\n },\n calculateReconnectDelay: (state) => {\n const seconds = Math.pow(2, Math.min(state.attempt, MAX_EXPONENTIAL_DELAY));\n const delay = seconds * 1_000;\n const logMessage = `[StreamingTransport] Scheduling reconnect attempt #${state.attempt} in ${delay}ms.`;\n if (state.attempt <= 5) {\n this.logger.info(logMessage);\n } else {\n this.logger.warn(logMessage);\n }\n return delay;\n },\n onDisconnect: () => {\n this.logger.debug(\"[StreamingTransport] Disconnected\");\n },\n });\n this.eventSource.connect();\n });\n this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), HEARTBEAT_INTERVAL_MS);\n let connectTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n eventSourcePromise,\n new Promise<this>((resolve) => {\n connectTimer = setTimeout(() => resolve(this), timeout);\n }),\n ]);\n } finally {\n clearTimeout(connectTimer);\n }\n }\n\n public get sessionId(): string | undefined {\n return this._sessionId;\n }\n\n private sendHeartbeat(): void {\n if (!this.isConnected || !this._sessionId) {\n return;\n }\n fetchWithTimeout(\n HEARTBEAT_INTERVAL_MS,\n this.heartbeatUrl,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n serverSdkKey: this.options.serverSdkKey,\n sessionId: this._sessionId,\n }),\n },\n this.logger,\n )\n .then((response) => {\n if (!response.ok) {\n this.logger.debug(`[StreamingTransport] Heartbeat failed with status: ${response.status}`);\n }\n })\n .catch((error) => {\n this.logger.debug(\"[StreamingTransport] Heartbeat failed: \", error);\n });\n }\n\n private buildRequestBody(): string {\n this._sessionId = randomUUID();\n return JSON.stringify({\n metaContext: this.options.metaContext,\n serverSdkKey: this.options.serverSdkKey,\n sessionId: this._sessionId,\n });\n }\n\n private dispatchMessage(data: string) {\n try {\n const json = JSON.parse(data);\n this.eventEmitter.emit(\"configBundleReceived\", json as ConfigBundle);\n } catch (error) {\n this.logger.error(\"[StreamingTransport] Error parsing and dispatching config data update: \", error);\n }\n }\n\n private prepareFatalError(\n responseStatus: number | undefined,\n error: Error | undefined,\n ): ConfigDirectorConnectionError {\n const status = responseStatus ?? 0;\n const headline = `Connection failed with status: ${responseStatus ?? \"unknown\"}.`;\n const errorLine = error ? ` Error: ${error}.` : \"\";\n const message = `${headline}${errorLine} This is an unrecoverable error, will not attempt to reconnect.`;\n return new ConfigDirectorConnectionError(message, status);\n }\n\n private isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500 && status != 429;\n }\n\n public get isConnected(): boolean {\n return this.eventSource?.readyState == \"open\";\n }\n\n public on<TName extends keyof TransportEvents>(\n name: TName,\n handler: (payload: TransportEvents[TName]) => void,\n ): void {\n this.eventEmitter.on(name, handler);\n }\n\n public off<TName extends keyof TransportEvents>(\n name: TName,\n handler?: ((payload: TransportEvents[TName]) => void) | undefined,\n ): void {\n if (handler) {\n this.eventEmitter.off(name, handler);\n } else {\n this.eventEmitter.removeAllListeners(name);\n }\n }\n\n public clear(): void {\n this.eventEmitter.removeAllListeners();\n }\n\n private stopHeartbeat() {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n\n public close() {\n this.stopHeartbeat();\n this.eventSource?.close();\n }\n\n public dispose(): void {\n this.close();\n this.clear();\n }\n}\n","import { ConfigDirectorConnectionError } from \"../errors\";\n\nexport abstract class AbstractPollingTransport {\n protected fatalError = false;\n protected pollingInterval: ReturnType<typeof setInterval> | undefined;\n protected abstract pollingIntervalSeconds: number;\n\n protected clearPollingInterval() {\n clearInterval(this.pollingInterval);\n }\n\n protected schedulePollingInterval(callback: () => void) {\n clearInterval(this.pollingInterval);\n if (this.pollingIntervalSeconds <= 0) {\n return;\n }\n this.pollingInterval = setInterval(callback, this.pollingIntervalSeconds * 1_000);\n }\n\n protected async handleNonOkResponse(response: Response) {\n if (!response.ok) {\n if (this.isStatusFatal(response.status)) {\n this.fatalError = true;\n this.close();\n throw this.prepareFatalResponseStatusError(response.status, await response.text());\n } else {\n throw new ConfigDirectorConnectionError(\n `Connection failed with status: ${response.status}`,\n response.status,\n );\n }\n }\n }\n\n protected handleFetchError(fetchError: unknown) {\n if (this.isFetchErrorFatal(fetchError)) {\n this.fatalError = true;\n this.close();\n throw new ConfigDirectorConnectionError(\n `Connection failed with fatal error: ${fetchError}. This is an unrecoverable error, retry attempts will be ignored.`,\n );\n } else if (fetchError instanceof SyntaxError) {\n throw new ConfigDirectorConnectionError(`Failed to parse the response from the server: ${fetchError}`);\n } else {\n throw new ConfigDirectorConnectionError(`Connection failed with error: ${fetchError}.`);\n }\n }\n\n protected prepareFatalResponseStatusError(\n responseStatus: number,\n errorBody: string | undefined,\n ): ConfigDirectorConnectionError {\n const status = responseStatus ?? 0;\n const headline = `Connection failed with status: ${responseStatus ?? \"unknown\"}`;\n const serverBody = (errorBody?.trim()?.length ?? 0) > 0 ? ` (${errorBody})` : \"\";\n const message = `${headline}${serverBody}. This is an unrecoverable error, retry attempts will be ignored.`;\n return new ConfigDirectorConnectionError(message, status);\n }\n\n protected isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500 && status != 429;\n }\n\n protected isFetchErrorFatal(fetchError: unknown): boolean {\n return (fetchError as any)?.name === \"NotAllowedError\";\n }\n\n public get isConnected(): boolean {\n return this.pollingInterval !== undefined;\n }\n\n public close() {\n clearInterval(this.pollingInterval);\n this.pollingInterval = undefined;\n }\n\n public abstract clear(): void;\n\n public dispose(): void {\n this.close();\n this.clear();\n }\n}\n","import type {\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { AbstractPollingTransport } from \"@shared/transport/AbstractPollingTransport\";\nimport { EventEmitter } from \"node:events\";\nimport { randomUUID } from \"node:crypto\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\nimport { SERVER_DEFAULT_POLLING_INTERVAL_SECONDS } from \"@shared/constants\";\n\nexport class PollingTransport extends AbstractPollingTransport implements Transport {\n protected pollingIntervalSeconds: number;\n private logger: ConfigDirectorLogger;\n private eventEmitter = new EventEmitter();\n private url: URL;\n private lastUpdateTimestamp: string | undefined;\n private readonly _sessionId = randomUUID();\n\n constructor(private readonly options: TransportOptions) {\n super();\n this.options = options;\n this.logger = options.logger;\n this.url = new URL(\"server/polling/v1\", options.baseUrl);\n this.pollingIntervalSeconds = options.pollingInterval ?? SERVER_DEFAULT_POLLING_INTERVAL_SECONDS;\n }\n\n public async connect(timeout: number): Promise<this> {\n this.clearPollingInterval();\n\n try {\n await this.fetchConfigs(timeout);\n } finally {\n // A transient failure on the first fetch must not leave the SDK without a\n // connection, so polling is scheduled either way. An unrecoverable failure has\n // already closed the transport and must not be retried.\n if (!this.fatalError) {\n this.schedulePollingInterval(() => {\n void this.fetchConfigs(timeout).catch((error) => {\n this.logger.error(\"[PollingTransport] Error during polling:\", error);\n });\n });\n }\n }\n\n return this;\n }\n\n public get sessionId(): string {\n return this._sessionId;\n }\n\n private async fetchConfigs(timeout: number) {\n if (this.fatalError) {\n this.logger.warn(\n \"[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.\",\n );\n return;\n }\n\n try {\n const response = await fetchWithTimeout(\n timeout,\n this.url,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n metaContext: this.options.metaContext,\n serverSdkKey: this.options.serverSdkKey,\n lastUpdateTimestamp: this.lastUpdateTimestamp,\n sessionId: this._sessionId,\n }),\n },\n this.logger,\n );\n\n await this.handleNonOkResponse(response);\n\n if (response.status === 200) {\n const json = JSON.parse(await response.text());\n this.lastUpdateTimestamp = json.timestamp;\n this.eventEmitter.emit(\"configBundleReceived\", json);\n }\n } catch (fetchError) {\n try {\n this.handleFetchError(fetchError);\n } catch (handledError) {\n if (this.fatalError) {\n this.eventEmitter.emit(\"connectionError\", handledError);\n }\n throw handledError;\n }\n }\n }\n\n public on<TName extends keyof TransportEvents>(\n name: TName,\n handler: (payload: TransportEvents[TName]) => void,\n ): void {\n this.eventEmitter.on(name, handler);\n }\n\n public off<TName extends keyof TransportEvents>(\n name: TName,\n handler?: ((payload: TransportEvents[TName]) => void) | undefined,\n ): void {\n if (handler) {\n this.eventEmitter.off(name, handler);\n } else {\n this.eventEmitter.removeAllListeners(name);\n }\n }\n\n public clear(): void {\n this.eventEmitter.removeAllListeners();\n }\n}\n","import type { ConfigType } from \"./types\";\nimport { ConfigDirectorValidationError } from \"./errors\";\n\nexport type NativeJsonValue = boolean | number | string | null | NativeJsonValue[] | { [key: string]: NativeJsonValue };\n\nexport type NativeValue = boolean | number | string | NativeJsonValue[] | { [key: string]: NativeJsonValue };\n\nexport type NativeValues = Record<string, NativeValue>;\n\nexport type EncodedNativeValue = { type: ConfigType; value: string };\n\nexport const encodeNativeValue = (key: string, value: NativeValue): EncodedNativeValue => {\n if (key.trim().length == 0) {\n throw new ConfigDirectorValidationError(\"Invalid config key. The key of a value cannot be empty.\");\n }\n return encode(key, value);\n};\n\nconst decoders: { [T in ConfigType]: (value: string) => NativeValue } = {\n boolean: (value) => value === \"true\",\n integer: Number,\n float: Number,\n string: (value) => value,\n enum: (value) => value,\n url: (value) => value,\n custom: (value) => value,\n json: (value) => JSON.parse(value),\n};\n\nexport const decodeNativeValue = (type: ConfigType, value: string): NativeValue => decoders[type](value);\n\nconst encode = (key: string, value: NativeValue): EncodedNativeValue => {\n switch (typeof value) {\n case \"boolean\":\n return { type: \"boolean\", value: value ? \"true\" : \"false\" };\n case \"number\":\n return encodeNumber(key, value);\n case \"string\":\n return { type: \"string\", value };\n case \"object\":\n if (value !== null) {\n assertJsonContents(key, value, \"\");\n return { type: \"json\", value: JSON.stringify(value) };\n }\n }\n throw new ConfigDirectorValidationError(\n `Invalid value for '${key}': ${describe(value)}. Use a boolean, a number, a string, a plain object, or an array.`,\n );\n};\n\nconst encodeNumber = (key: string, value: number): EncodedNativeValue => {\n if (!Number.isFinite(value)) {\n throw new ConfigDirectorValidationError(`Invalid value for '${key}': ${describe(value)} is not a finite number.`);\n }\n if (Number.isInteger(value)) {\n if (!Number.isSafeInteger(value)) {\n throw new ConfigDirectorValidationError(\n `Invalid value for '${key}': ${value} is not a safe integer, so the SDK could not read it back exactly.`,\n );\n }\n return { type: \"integer\", value: String(value) };\n }\n return { type: \"float\", value: toPlainDecimal(value) };\n};\n\nconst toPlainDecimal = (value: number): string => {\n const text = String(value);\n const exponentIndex = text.indexOf(\"e-\");\n if (exponentIndex < 0) {\n return text;\n }\n const sign = text.startsWith(\"-\") ? \"-\" : \"\";\n const [integerDigits = \"\", fractionDigits = \"\"] = text.slice(sign.length, exponentIndex).split(\".\");\n const leadingZeros = Number(text.slice(exponentIndex + 2)) - integerDigits.length;\n return `${sign}0.${\"0\".repeat(leadingZeros)}${integerDigits}${fractionDigits}`;\n};\n\nconst assertJsonContents = (key: string, value: unknown, path: string): void => {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") {\n return;\n }\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw invalidJsonContents(key, path, `${describe(value)} is not a finite number`);\n }\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((item, index) => assertJsonContents(key, item, `${path}[${index}]`));\n return;\n }\n if (isPlainObject(value)) {\n for (const [property, item] of Object.entries(value)) {\n assertJsonContents(key, item, path ? `${path}.${property}` : property);\n }\n return;\n }\n throw invalidJsonContents(key, path, `${describe(value)} cannot be encoded as JSON`);\n};\n\nconst invalidJsonContents = (key: string, path: string, problem: string) =>\n new ConfigDirectorValidationError(\n `Invalid value for '${key}'${path ? ` at '${path}'` : \"\"}: ${problem}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`,\n );\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst describe = (value: unknown): string => {\n if (value === null) {\n return \"null\";\n }\n if (typeof value === \"function\") {\n return \"a function\";\n }\n if (typeof value === \"object\") {\n return `a ${value.constructor?.name ?? \"object\"} instance`;\n }\n if (typeof value === \"symbol\") {\n return \"a symbol\";\n }\n return String(value);\n};\n","import type { ConfigDefinition } from \"./types\";\nimport { encodeNativeValue, type NativeValue } from \"@shared/native-values\";\n\nexport const unconditionalConfig = (id: string, key: string, value: NativeValue): ConfigDefinition => {\n const encoded = encodeNativeValue(key, value);\n return {\n id,\n key,\n type: encoded.type,\n variations: [{ name: null, value: encoded.value }],\n target: { defaultValue: encoded.value, rules: [] },\n };\n};\n","import EventEmitter from \"node:events\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { ConfigDirectorInitializationError, ConfigDirectorValidationError } from \"@shared/errors\";\nimport type { ConfigDefinition, OfflineValues, Transport, TransportEvents } from \"./types\";\nimport type { OfflineSource } from \"./connection-plan\";\nimport { unconditionalConfig } from \"./native-values\";\n\nconst OFFLINE_ENVIRONMENT = { environmentId: \"offline\", projectId: \"offline\" };\n\nexport class OfflineTransport implements Transport {\n private readonly emitter = new EventEmitter();\n private connected = false;\n\n constructor(private readonly source: OfflineSource) {}\n\n public async connect(): Promise<this> {\n const configs = await loadOfflineConfigs(this.source);\n this.connected = true;\n this.emitter.emit(\"configBundleReceived\", { ...OFFLINE_ENVIRONMENT, kind: \"full\", configs });\n return this;\n }\n\n public get isConnected(): boolean {\n return this.connected;\n }\n\n public on<TName extends keyof TransportEvents>(name: TName, handler: (payload: TransportEvents[TName]) => void) {\n this.emitter.on(name, handler);\n }\n\n public off<TName extends keyof TransportEvents>(name: TName, handler?: (payload: TransportEvents[TName]) => void) {\n if (handler) {\n this.emitter.off(name, handler);\n } else {\n this.emitter.removeAllListeners(name);\n }\n }\n\n public close() {\n this.connected = false;\n }\n\n public dispose() {\n this.close();\n this.emitter.removeAllListeners();\n }\n}\n\nconst loadOfflineConfigs = async (source: OfflineSource): Promise<Record<string, ConfigDefinition>> => {\n switch (source.kind) {\n case \"in-code-defaults\":\n return {};\n case \"values\":\n return encodeOfflineValues(source.values, \"The offline values\");\n case \"file\":\n return encodeOfflineValues(await readOfflineValuesFile(source.path), `The offline values file '${source.path}'`);\n case \"rejected\":\n throw new ConfigDirectorInitializationError(source.message);\n }\n};\n\nconst readOfflineValuesFile = async (path: string): Promise<OfflineValues> => {\n let text: string;\n try {\n text = await readFile(resolve(path), \"utf8\");\n } catch (error) {\n throw new ConfigDirectorInitializationError(\n `The offline values file '${path}' could not be read: ${messageOf(error)}`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n throw new ConfigDirectorInitializationError(\n `The offline values file '${path}' is not valid JSON: ${messageOf(error)}`,\n );\n }\n if (!isJsonObject(parsed)) {\n throw new ConfigDirectorInitializationError(\n `The offline values file '${path}' must hold a JSON object with one value per config key, but holds ${describeJson(parsed)}.`,\n );\n }\n return parsed;\n};\n\nconst encodeOfflineValues = (values: OfflineValues, subject: string): Record<string, ConfigDefinition> => {\n try {\n return Object.fromEntries(\n Object.entries(values).map(([key, value]) => [key, unconditionalConfig(`offline:${key}`, key, value)]),\n );\n } catch (error) {\n if (error instanceof ConfigDirectorValidationError) {\n throw new ConfigDirectorInitializationError(`${subject} could not be loaded. ${error.message}`);\n }\n throw error;\n }\n};\n\nconst isJsonObject = (parsed: unknown): parsed is OfflineValues =>\n typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed);\n\nconst describeJson = (parsed: unknown): string => {\n if (parsed === null) {\n return \"null\";\n }\n if (Array.isArray(parsed)) {\n return \"an array\";\n }\n return `a ${typeof parsed}`;\n};\n\nconst messageOf = (error: unknown): string => (error instanceof Error ? error.message : String(error));\n","import { ConfigDirectorValidationError } from \"@shared/errors\";\nimport type { ConnectionOptions, OfflineValues } from \"./types\";\n\nexport type LiveConnectionMode = \"streaming\" | \"polling\";\n\nexport type OfflineSource =\n | { kind: \"in-code-defaults\" }\n | { kind: \"file\"; path: string }\n | { kind: \"values\"; values: OfflineValues }\n | { kind: \"rejected\"; message: string };\n\nexport type ConnectionPlan =\n | { kind: \"connect\"; mode: LiveConnectionMode; serverSdkKey: string; baseUrl: URL }\n | { kind: \"stay-offline\"; source: OfflineSource };\n\nconst defaultBaseUrl = new URL(\"https://server-sdk-api.configdirector.com\");\n\nexport const planConnection = (\n serverSdkKey: string | undefined,\n connection: ConnectionOptions | undefined,\n): ConnectionPlan => {\n if (connection?.mode === \"offline\") {\n return { kind: \"stay-offline\", source: offlineSourceOf(connection) };\n }\n const mode = connection?.mode ?? \"streaming\";\n if (!serverSdkKey || serverSdkKey.trim().length == 0) {\n throw new ConfigDirectorValidationError(\n \"No server SDK key was provided, the client cannot be instantiated without a valid server SDK key\",\n );\n }\n if (connection?.file !== undefined || connection?.values !== undefined) {\n return {\n kind: \"stay-offline\",\n source: {\n kind: \"rejected\",\n message: `The connection options 'file' and 'values' apply only when the connection mode is 'offline', but the mode is '${mode}'.`,\n },\n };\n }\n return { kind: \"connect\", mode, serverSdkKey, baseUrl: baseUrlOf(connection?.url) };\n};\n\nconst baseUrlOf = (url: string | undefined): URL => {\n if (!url) {\n return defaultBaseUrl;\n }\n try {\n return new URL(url);\n } catch (error) {\n throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);\n }\n};\n\nconst offlineSourceOf = (connection: ConnectionOptions): OfflineSource => {\n if (connection.file !== undefined && connection.values !== undefined) {\n return { kind: \"rejected\", message: \"An offline connection takes either 'file' or 'values', not both.\" };\n }\n if (connection.file !== undefined) {\n return { kind: \"file\", path: connection.file };\n }\n if (connection.values !== undefined) {\n return { kind: \"values\", values: connection.values };\n }\n return { kind: \"in-code-defaults\" };\n};\n","const ARRAY_INDEX_PATTERN = /^(?:0|[1-9][0-9]*)$/;\n\nconst INVALID_ESCAPE_PATTERN = /~(?![01])/;\n\nconst isSerializedMember = Object.prototype.propertyIsEnumerable;\n\nconst decodeReferenceToken = (token: string): string | undefined => {\n if (INVALID_ESCAPE_PATTERN.test(token)) {\n return undefined;\n }\n return token.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n};\n\nconst step = (current: unknown, token: string): unknown => {\n if (Array.isArray(current)) {\n return ARRAY_INDEX_PATTERN.test(token) && isSerializedMember.call(current, token)\n ? current[Number(token)]\n : undefined;\n }\n if (typeof current === \"object\" && current !== null) {\n return isSerializedMember.call(current, token) ? (current as Record<string, unknown>)[token] : undefined;\n }\n return undefined;\n};\n\nexport const resolveJsonPointer = (pointer: string, document: unknown): unknown => {\n if (pointer === \"\") {\n return document;\n }\n if (!pointer.startsWith(\"/\")) {\n return undefined;\n }\n let current = document;\n for (const encodedToken of pointer.slice(1).split(\"/\")) {\n const token = decodeReferenceToken(encodedToken);\n if (token === undefined) {\n return undefined;\n }\n current = step(current, token);\n if (current === undefined) {\n return undefined;\n }\n }\n return current;\n};\n","import type { Operator } from \"./types\";\n\n/**\n * Text comparisons.\n *\n * `value` is always a string: an absent attribute has already been rendered to \"\".\n *\n * An empty `targetValues` makes the single-target operators false — there is nothing to compare\n * against — while the \"any of\" operators fall out naturally, so \"is NOT one of nothing\" is true.\n */\nexport const compareText = (value: string, operator: Operator, targetValues: string[]) => {\n const first: string | undefined = targetValues[0];\n\n switch (operator.toLowerCase()) {\n case \"=\":\n case \"equals\":\n return first !== undefined && value === first;\n case \"!=\":\n case \"does not equal\":\n return first !== undefined && value !== first;\n case \"is one of\":\n return targetValues.includes(value);\n case \"is not one of\":\n return !targetValues.includes(value);\n case \"starts with any of\":\n return targetValues.some((target) => value.startsWith(target));\n case \"does not start with any of\":\n return !targetValues.some((target) => value.startsWith(target));\n case \"ends with any of\":\n return targetValues.some((target) => value.endsWith(target));\n case \"does not end with any of\":\n return !targetValues.some((target) => value.endsWith(target));\n default:\n return false;\n }\n};\n","import type { Operator } from \"./types\";\n\n/**\n * Numeric comparisons.\n *\n * A value that is not a finite number, the wrong JSON type, unparseable text, or an infinity,\n * makes every operator false except `!=`, which is true. It does not throw: a rule that targets a\n * numeric trait some users do not have should skip those users, not be discarded.\n */\nexport const compareNumeric = (givenValue: unknown, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n\n const value = parseFinite(givenValue);\n if (value === undefined) {\n return lowercaseOperator === \"!=\" || lowercaseOperator === \"does not equal\";\n }\n\n const first: string | undefined = targetValues[0];\n if (first === undefined) {\n return false;\n }\n const target = parseFinite(first);\n if (target === undefined) {\n return false;\n }\n\n switch (lowercaseOperator) {\n case \"=\":\n case \"equals\":\n return value === target;\n case \"!=\":\n case \"does not equal\":\n return value !== target;\n case \"<\":\n return value < target;\n case \"<=\":\n return value <= target;\n case \">\":\n return value > target;\n case \">=\":\n return value >= target;\n default:\n return false;\n }\n};\n\n/**\n * Parse strictly: no surrounding whitespace, no trailing characters, decimal notation only.\n * `10`, `10.5`, `-5` and `1e3` parse; `26abc`, ` 42 `, `0x10`, `Infinity` and `\"\"` do not.\n */\nconst parseFinite = (value: unknown): number | undefined => {\n // Booleans are not numbers here, even though JavaScript would happily coerce them.\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? value : undefined;\n }\n if (typeof value !== \"string\" || value.length === 0) {\n return undefined;\n }\n // Number() would accept surrounding whitespace, `0x10`, `Infinity`, and returns 0 for a blank\n // string. Restricting the characters first rules those out; Number() then validates the\n // grammar, rejecting the likes of `1.2.3` and `1e`. Both steps are a single pass.\n if (!isDecimalCharacters(value)) {\n return undefined;\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n};\n\nconst isDecimalCharacters = (value: string): boolean => {\n for (const character of value) {\n const isDigit = character >= \"0\" && character <= \"9\";\n if (!isDigit && !\"+-.eE\".includes(character)) {\n return false;\n }\n }\n return true;\n};\n","import type { Operator } from \"./types\";\nimport { DateTime } from \"luxon\";\n\n/**\n * Date and time comparisons.\n *\n * Parsing is delegated to Luxon rather than to `new Date`, for two reasons. `new Date` reads a\n * date-time with no offset as *local* time, which would make an evaluation depend on the\n * timezone of whichever machine ran it; `{ zone: \"utc\" }` gives the required reading directly.\n * And `new Date` rolls impossible dates over — `2026-02-30` becomes March 2nd — where the\n * ConfigDirector server and the Python SDK both reject them.\n *\n * Returns false whenever either side is missing or unparseable.\n */\nexport const compareDate = (value: string, operator: Operator, targetValues: string[]) => {\n const first: string | undefined = targetValues[0];\n if (first === undefined) {\n return false;\n }\n\n const dateValue = parseDate(value);\n const dateTarget = parseDate(first);\n if (dateValue === undefined || dateTarget === undefined) {\n return false;\n }\n\n switch (operator) {\n case \"is after\":\n return dateValue > dateTarget;\n case \"is before\":\n return dateValue < dateTarget;\n default:\n return false;\n }\n};\n\n/** The instant as milliseconds since the epoch, or undefined when the value is not a date. */\nconst parseDate = (value: string): number | undefined => {\n const parsed = DateTime.fromISO(value, { zone: \"utc\" });\n return parsed.isValid ? parsed.toMillis() : undefined;\n};\n","import type { Operator } from \"./types\";\nimport * as semver from \"semver\";\n\n/**\n * Semantic version comparisons.\n *\n * Both sides are coerced first, so partial versions such as `1.0` and `v`-prefixed ones are\n * usable, and any prerelease or build suffix is dropped. A value that cannot be coerced matches\n * nothing, which makes `is NOT one of` the only operator that can be true for it.\n */\nexport const compareSemver = (value: string, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n if (value.trim().length === 0) {\n return lowercaseOperator === \"is not one of\";\n }\n\n const version = semver.coerce(value);\n const targets = targetValues.map((target) => semver.coerce(target));\n const first: semver.SemVer | null = targets[0] ?? null;\n\n switch (lowercaseOperator) {\n case \"=\":\n return equals(version, first);\n case \"<\":\n return ordered(version, first, semver.lt);\n case \"<=\":\n return ordered(version, first, semver.lte);\n case \">\":\n return ordered(version, first, semver.gt);\n case \">=\":\n return ordered(version, first, semver.gte);\n case \"is one of\":\n return targets.some((target) => equals(version, target));\n case \"is not one of\":\n return !targets.some((target) => equals(version, target));\n default:\n return false;\n }\n};\n\n/** An operand that could not be coerced never compares equal. */\nconst equals = (value: semver.SemVer | null, target: semver.SemVer | null): boolean =>\n value !== null && target !== null && semver.eq(value, target);\n\n/** An operand that could not be coerced never satisfies an ordering comparison. */\nconst ordered = (\n value: semver.SemVer | null,\n target: semver.SemVer | null,\n compare: (a: semver.SemVer, b: semver.SemVer) => boolean,\n): boolean => value !== null && target !== null && compare(value, target);\n","/**\n * Rendering a resolved context value to text.\n */\nimport type { ResolvedType } from \"./types\";\n\n/** A value the context does not carry. Compared as \"\", so a negative operator can still match. */\nexport const ABSENT = Symbol(\"absent\");\n\n/** An attribute this SDK version does not know about. Not compared at all. */\nexport const UNKNOWN_ATTRIBUTE = Symbol(\"unknown-attribute\");\n\nexport type Resolved = unknown | typeof ABSENT;\n\nexport const isScalar = (value: unknown): value is string | number | boolean =>\n typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\";\n\n/**\n * Render a resolved value to text. Absent values, and values with no meaningful text form —\n * arrays, objects, null — render to the empty string rather than to something like\n * \"[object Object]\".\n */\nexport const render = (value: Resolved): string => {\n if (value === ABSENT || value === null || value === undefined) {\n return \"\";\n }\n return isScalar(value) ? renderScalar(value) : \"\";\n};\n\n/** Render a scalar the way JSON would spell it: `true`, `26`, `26.5`. */\nexport const renderScalar = (value: string | number | boolean): string => {\n if (typeof value === \"string\") {\n return value;\n }\n return String(value);\n};\n\n/** The raw value, for comparisons that inspect its JSON type rather than its text. */\nexport const unwrap = (value: Resolved): unknown => (value === ABSENT ? undefined : value);\n\nexport const resolvedTypeOf = (value: Resolved): ResolvedType => {\n if (value === UNKNOWN_ATTRIBUTE) {\n return \"unknown-attribute\";\n }\n if (value === ABSENT) {\n return \"absent\";\n }\n if (isScalar(value)) {\n return \"scalar\";\n }\n return Array.isArray(value) ? \"array\" : \"object\";\n};\n","import type { Operator } from \"./types\";\nimport { isScalar, renderScalar } from \"./render\";\n\n/**\n * Array membership comparisons.\n *\n * Elements are rendered to text before matching, so a list of numbers matches a target of \"1\".\n * A value that is not an array — including a comma-separated string — contains nothing, so only\n * the negative operator can be true for it.\n */\nexport const compareArray = (givenValue: unknown, operator: Operator, targetValues: string[]) => {\n const lowercaseOperator = operator.toLowerCase();\n if (!Array.isArray(givenValue)) {\n return lowercaseOperator === \"does not contain any of\";\n }\n\n // Nested arrays, objects, and nulls have no text form, so they are dropped rather than matching\n // an empty target value.\n const elements = givenValue.filter(isScalar).map(renderScalar);\n\n switch (lowercaseOperator) {\n case \"contains any of\":\n return elements.some((element) => targetValues.includes(element));\n case \"does not contain any of\":\n return !elements.some((element) => targetValues.includes(element));\n default:\n return false;\n }\n};\n","import type { EvaluationContext } from \"./types\";\n\nexport const USER_CONTEXT_KIND = \"user\";\n\nexport const effectiveContextKind = (context: EvaluationContext | undefined): string =>\n context?.context?.kind ?? USER_CONTEXT_KIND;\n","import type { AttributeCondition, ConditionCheck, EvaluationContext } from \"./types\";\nimport { resolveJsonPointer } from \"./json-pointer\";\nimport { compareText } from \"./text-comparison\";\nimport { compareNumeric } from \"./numeric-comparison\";\nimport { compareDate } from \"./date-comparison\";\nimport { compareSemver } from \"./semver-comparison\";\nimport { compareArray } from \"./array-comparison\";\nimport { effectiveContextKind } from \"./context-kind\";\nimport { ABSENT, UNKNOWN_ATTRIBUTE, render, resolvedTypeOf, unwrap, type Resolved } from \"./render\";\n\nexport class ConditionEvaluator {\n /**\n * Decide whether a condition holds for a context.\n *\n *\n * An attribute the context does not carry is not an error: it resolves to the empty string, so\n * a condition can still match or not match on its own terms.\n */\n public evaluate(condition: AttributeCondition, context?: EvaluationContext): boolean {\n return this.explain(condition, context).matched;\n }\n\n /**\n * Decide whether a condition holds for a context, and say what the condition was compared\n * against: the raw resolved value and its shape.\n */\n public explain(condition: AttributeCondition, context?: EvaluationContext): ConditionCheck {\n const value = this.resolve(condition, context);\n const resolvedType = resolvedTypeOf(value);\n const resolvedValue = value === ABSENT || value === UNKNOWN_ATTRIBUTE ? undefined : value;\n if (value === UNKNOWN_ATTRIBUTE) {\n return { matched: false, resolvedValue, resolvedType };\n }\n return { matched: this.compare(condition, value), resolvedValue, resolvedType };\n }\n\n private compare(condition: AttributeCondition, value: Resolved): boolean {\n const targetValues = condition.targetValues ?? [];\n\n switch (condition.targetType) {\n case \"text\":\n return compareText(render(value), condition.operator, targetValues);\n case \"number\":\n return compareNumeric(unwrap(value), condition.operator, targetValues);\n case \"datetime\":\n return compareDate(render(value), condition.operator, targetValues);\n case \"semver\":\n return compareSemver(render(value), condition.operator, targetValues);\n case \"array\":\n return compareArray(unwrap(value), condition.operator, targetValues);\n }\n\n return false;\n }\n\n /** The raw value a condition targets, or one of the two sentinels. */\n private resolve(condition: AttributeCondition, context?: EvaluationContext): Resolved {\n switch (condition.attribute) {\n case \"identifier\":\n return orAbsent(context?.context?.id);\n case \"name\":\n return orAbsent(context?.context?.name);\n case \"kind\":\n return effectiveContextKind(context);\n case \"appName\":\n return orAbsent(context?.metadata?.appName);\n case \"appVersion\":\n return orAbsent(context?.metadata?.appVersion);\n case \"traits\":\n if (!condition.trait) {\n return ABSENT;\n }\n return orAbsent(resolveJsonPointer(condition.trait, context?.context?.traits));\n default:\n return UNKNOWN_ATTRIBUTE;\n }\n }\n}\n\nconst orAbsent = (value: unknown): Resolved => (value === undefined || value === null ? ABSENT : value);\n","import type { ConditionEvaluator } from \"./ConditionEvaluator\";\nimport type { EvaluationContext, Segment, SegmentCheck, SegmentCondition, Segments } from \"./types\";\n\nexport class SegmentEvaluator {\n constructor(private readonly conditionEvaluator: ConditionEvaluator) {}\n\n public explain(\n condition: SegmentCondition,\n segments: Segments | undefined,\n context?: EvaluationContext,\n ): SegmentCheck {\n const segment = segments?.[condition.segmentId];\n if (segment === undefined || !holdsAttributeConditionsOnly(segment)) {\n return { matched: false, segmentFound: false, matchedGroupIndex: undefined };\n }\n const matchedGroupIndex = this.matchingGroupIndex(segment, context);\n const member = matchedGroupIndex !== undefined;\n return { matched: this.operatorHolds(condition, member), segmentFound: true, matchedGroupIndex };\n }\n\n private operatorHolds(condition: SegmentCondition, member: boolean): boolean {\n switch (condition.operator.toLowerCase()) {\n case \"in\":\n return member;\n case \"not in\":\n return !member;\n default:\n return false;\n }\n }\n\n private matchingGroupIndex(segment: Segment, context?: EvaluationContext): number | undefined {\n const index = (segment.groups ?? []).findIndex((group) =>\n (group ?? []).every((condition) => this.conditionEvaluator.evaluate(condition, context)),\n );\n return index === -1 ? undefined : index;\n }\n}\n\nconst holdsAttributeConditionsOnly = (segment: Segment): boolean =>\n (segment.groups ?? []).every((group) =>\n (group ?? []).every((condition) => condition.kind === undefined || condition.kind === \"attribute\"),\n );\n","import { rapidhash } from \"rapidhash-js\";\nimport { USER_CONTEXT_KIND } from \"./context-kind\";\n\nconst SEED = 0x397832987n;\n\nexport const PERCENTAGE_WITHOUT_IDENTIFIER = 0.0;\n\nexport const assignPercentage = ({\n configId,\n contextIdentifier,\n contextKind,\n}: {\n configId: string;\n contextIdentifier: string;\n contextKind: string;\n}): number => {\n const hashInput =\n contextKind === USER_CONTEXT_KIND\n ? `${contextIdentifier}-${configId}`\n : `${contextIdentifier}-${configId}:${contextKind}`;\n\n return Number(rapidhash(hashInput, { seed: SEED }) % 1_000n) / 10.0;\n};\n","import { ConditionEvaluator } from \"./ConditionEvaluator\";\nimport { SegmentEvaluator } from \"./SegmentEvaluator\";\nimport { assignPercentage, PERCENTAGE_WITHOUT_IDENTIFIER } from \"./percent-hashing\";\nimport { effectiveContextKind } from \"./context-kind\";\nimport type {\n Condition,\n ConditionExplanation,\n ConditionalRule,\n Config,\n EvaluationContext,\n ConfigState,\n Percentage,\n Rule,\n ConfigDirectorLogger,\n EvaluationExplanation,\n RuleExplanation,\n Segments,\n Share,\n} from \"./types\";\n\ntype RuleResult = {\n explanation: RuleExplanation;\n value: string | undefined;\n};\n\nexport class ConfigEvaluator {\n private readonly conditionEvaluator = new ConditionEvaluator();\n private readonly segmentEvaluator = new SegmentEvaluator(this.conditionEvaluator);\n\n constructor(private readonly logger: ConfigDirectorLogger) {\n if (!logger || !logger.warn || !logger.error || !logger.info) {\n throw new TypeError(\"The provided logger is not a valid 'ConfigDirectorLogger'\");\n }\n\n this.logger = logger;\n }\n\n public evaluate(config: Config, context?: EvaluationContext, segments?: Segments): ConfigState {\n return {\n id: config.id,\n key: config.key,\n type: config.type,\n value: this.explain(config, context, segments).value,\n };\n }\n\n /**\n * Evaluate a config for a context and record how the value was reached: every rule in the\n * order it was walked with its outcome, the conditions that were checked with what they\n * resolved to, and the share a rollout assigned the context to. `evaluate` is this walk with\n * only the value kept, so the two cannot disagree. `segments` is the payload's segments map,\n * which a segment condition looks its segment up in; a segment it does not hold never matches.\n */\n public explain(config: Config, context?: EvaluationContext, segments?: Segments): EvaluationExplanation {\n const rules = [...(config.target?.rules ?? [])].sort(\n (a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER),\n );\n const explanations: RuleExplanation[] = [];\n let served: { ruleId: string; value: string } | undefined;\n for (const rule of rules) {\n if (served) {\n explanations.push({ ruleId: rule.id, outcome: \"not-evaluated\", conditions: [], bucket: undefined });\n continue;\n }\n const result = this.explainRule(rule, config, context, segments);\n explanations.push(result.explanation);\n if (result.value !== undefined) {\n served = { ruleId: rule.id, value: result.value };\n }\n }\n return {\n value: served?.value ?? config.target?.defaultValue,\n servedBy: served ? { kind: \"rule\", ruleId: served.ruleId } : { kind: \"default\" },\n rules: explanations,\n };\n }\n\n private explainRule(\n rule: Rule,\n config: Config,\n context: EvaluationContext | undefined,\n segments: Segments | undefined,\n ): RuleResult {\n const explanation: RuleExplanation = {\n ruleId: rule.id,\n outcome: \"not-matched\",\n conditions: [],\n bucket: undefined,\n };\n try {\n let value: string | undefined;\n if (rule.type == \"percentage\") {\n value = this.explainPercentage(rule.percentages ?? [], config, context, explanation);\n } else if (rule.type == \"conditional\") {\n value = this.explainConditionalRule(rule, config, context, segments, explanation);\n }\n if (value !== undefined) {\n explanation.outcome = \"matched\";\n }\n return { explanation, value };\n } catch (error) {\n this.logger.warn(\n `There was an error while evaluating a targeting rule '${rule?.id}' for '${config?.key}'. The rule will be disregarded.`,\n {\n error,\n configKey: config?.key,\n ruleId: rule?.id,\n },\n );\n return {\n explanation: { ruleId: rule.id, outcome: \"errored\", conditions: [], bucket: undefined },\n value: undefined,\n };\n }\n }\n\n private explainPercentage(\n percentages: Percentage[],\n config: Config,\n context: EvaluationContext | undefined,\n explanation: RuleExplanation,\n ): string | undefined {\n const identifier = context?.context?.id ?? undefined;\n const assignedPercentage =\n identifier === undefined\n ? PERCENTAGE_WITHOUT_IDENTIFIER\n : assignPercentage({\n configId: config.id,\n contextIdentifier: identifier,\n contextKind: effectiveContextKind(context),\n });\n const shares: Share[] = [];\n let sum = 0.0;\n let selected: Percentage | undefined = undefined;\n // A bucket spans [sum, sum + percentage). Strict, so a context landing exactly on a boundary\n // belongs to the bucket that starts there -- which is what keeps a 0% bucket unreachable and\n // each bucket's share exact. See SEMANTICS.md §7.1 in targeting-rules-contract.\n for (const percentage of percentages) {\n const to = sum + percentage.percentage;\n shares.push({ percentageId: percentage.id, from: sum, to, value: percentage.value?.toString() });\n if (selected === undefined && assignedPercentage < to) {\n selected = percentage;\n }\n sum = to;\n }\n\n explanation.bucket = {\n identifier,\n defaultedToPredefinedBucket: identifier === undefined,\n assignedPercentage,\n shares,\n selectedPercentageId: selected?.id,\n };\n return selected?.value != null ? selected.value.toString() : undefined;\n }\n\n private explainConditionalRule(\n rule: ConditionalRule,\n config: Config,\n context: EvaluationContext | undefined,\n segments: Segments | undefined,\n explanation: RuleExplanation,\n ): string | undefined {\n let failed = false;\n for (const condition of rule.conditions ?? []) {\n if (failed) {\n explanation.conditions.push({ conditionId: condition.id, outcome: \"not-evaluated\" });\n continue;\n }\n const checked = this.explainCondition(condition, context, segments);\n explanation.conditions.push(checked);\n failed = checked.outcome !== \"matched\";\n }\n\n if (failed) {\n return undefined;\n }\n if (rule.target == \"value\") {\n return rule.value != null ? rule.value.toString() : undefined;\n }\n if (rule.target == \"percentage\") {\n return this.explainPercentage(rule.percentages ?? [], config, context, explanation);\n }\n return undefined;\n }\n\n private explainCondition(\n condition: Condition,\n context: EvaluationContext | undefined,\n segments: Segments | undefined,\n ): ConditionExplanation {\n if (condition.kind === \"segment\") {\n const check = this.segmentEvaluator.explain(condition, segments, context);\n return {\n conditionId: condition.id,\n kind: \"segment\",\n outcome: check.matched ? \"matched\" : \"not-matched\",\n segmentId: condition.segmentId,\n segmentFound: check.segmentFound,\n matchedGroupIndex: check.matchedGroupIndex,\n };\n }\n const check = this.conditionEvaluator.explain(condition, context);\n return {\n conditionId: condition.id,\n kind: \"attribute\",\n outcome: check.matched ? \"matched\" : \"not-matched\",\n resolvedValue: check.resolvedValue,\n resolvedType: check.resolvedType,\n };\n }\n}\n","import type { ConfigState, ConfigType, ConfigValueType, EvaluationReason } from \"./types\";\n\ntype NativeType = \"string\" | \"number\" | \"bigint\" | \"boolean\" | \"symbol\" | \"undefined\" | \"object\" | \"function\";\n\ntype ParseResult<T extends ConfigValueType> = {\n parsedValue: T;\n parsedValueId: string | null | undefined;\n requestedType: NativeType | string;\n usedDefault: boolean;\n reason: EvaluationReason;\n};\n\nexport const getRequestedType = <T extends ConfigValueType>(defaultValue: T): string => {\n const baseType = typeof defaultValue;\n if (baseType === \"object\") {\n try {\n return (defaultValue as object).constructor?.name ?? baseType;\n } catch {\n return baseType;\n }\n } else if (baseType === \"function\") {\n try {\n const functionName = (defaultValue as any).name;\n return functionName ? `function: ${functionName}` : baseType;\n } catch {\n return baseType;\n }\n }\n return baseType;\n};\n\nconst isNumericNativeType = (requestedType: NativeType): boolean => {\n return requestedType === \"number\" || requestedType === \"bigint\";\n};\n\nconst configTypesNotReadableAsString: ReadonlySet<ConfigType> = new Set<ConfigType>([\"boolean\", \"integer\", \"float\"]);\n\nexport const parseConfigValue = <T extends ConfigValueType>(\n configState: ConfigState,\n defaultValue: T,\n): ParseResult<T> => {\n const value = configState.value;\n const requestedType = getRequestedType(defaultValue);\n\n if (value == null || value === \"\") {\n return {\n parsedValue: defaultValue as T,\n parsedValueId: undefined,\n requestedType,\n usedDefault: true,\n reason: \"value-missing\",\n };\n }\n\n if (configState.type === \"json\") {\n return parseJson(value, defaultValue, requestedType, configState.valueId);\n }\n\n if (typeof defaultValue === \"string\") {\n if (configTypesNotReadableAsString.has(configState.type)) {\n return {\n parsedValue: defaultValue,\n parsedValueId: undefined,\n requestedType,\n usedDefault: true,\n reason: \"type-mismatch\",\n };\n }\n return {\n parsedValue: value as T,\n parsedValueId: configState.valueId,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n }\n\n if (\n typeof defaultValue === \"boolean\" &&\n (configState.type === \"boolean\" || configState.type === \"string\" || configState.type === \"enum\")\n ) {\n const boolValue = parseConfigBoolean(value);\n const hasBoolean = typeof boolValue === \"boolean\";\n return {\n parsedValue: (hasBoolean ? boolValue : defaultValue) as T,\n parsedValueId: hasBoolean ? configState.valueId : undefined,\n requestedType,\n usedDefault: !hasBoolean,\n reason: hasBoolean ? \"found-match\" : \"invalid-boolean\",\n };\n }\n\n if (isNumericNativeType(typeof defaultValue) && configState.type === \"integer\") {\n const numValue = parseConfigInteger(value);\n const hasNumber = typeof numValue === \"number\";\n return {\n parsedValue: (hasNumber ? numValue : defaultValue) as T,\n parsedValueId: hasNumber ? configState.valueId : undefined,\n requestedType,\n usedDefault: !hasNumber,\n reason: hasNumber ? \"found-match\" : \"invalid-number\",\n };\n }\n if (\n isNumericNativeType(typeof defaultValue) &&\n (configState.type === \"float\" || configState.type === \"enum\" || configState.type === \"string\")\n ) {\n const numValue = parseConfigFloat(value);\n const hasNumber = typeof numValue === \"number\";\n return {\n parsedValue: (hasNumber ? numValue : defaultValue) as T,\n parsedValueId: hasNumber ? configState.valueId : undefined,\n requestedType,\n usedDefault: !hasNumber,\n reason: hasNumber ? \"found-match\" : \"invalid-number\",\n };\n }\n\n return {\n parsedValue: defaultValue,\n parsedValueId: undefined,\n requestedType,\n usedDefault: true,\n reason: \"type-mismatch\",\n };\n};\n\nconst parseConfigBoolean = (value: string): boolean | undefined => {\n if (!value) {\n return;\n }\n const lowerValue = value.toLowerCase();\n if (lowerValue != \"true\" && lowerValue != \"false\") {\n return;\n }\n return lowerValue === \"true\";\n};\n\nconst parseConfigInteger = (value: string): number | undefined => {\n const num = parseStrictNumber(value);\n return num == null ? undefined : Math.trunc(num);\n};\n\nconst parseConfigFloat = (value: string): number | undefined => {\n return parseStrictNumber(value);\n};\n\nconst parseStrictNumber = (value: string): number | undefined => {\n if (!value || !isDecimalCharacters(value)) {\n return;\n }\n const num = Number(value);\n if (isNaN(num) || !isFinite(num)) {\n return;\n }\n return num;\n};\n\nconst isDecimalCharacters = (value: string): boolean => {\n for (const character of value) {\n const isDigit = character >= \"0\" && character <= \"9\";\n if (!isDigit && !\"+-.eE\".includes(character)) {\n return false;\n }\n }\n return true;\n};\n\nconst parseJson = <T extends ConfigValueType>(\n jsonString: string,\n defaultValue: T,\n requestedType: string,\n valueId: string | null | undefined,\n): ParseResult<T> => {\n if (typeof defaultValue === \"string\") {\n return {\n parsedValue: jsonString as T,\n parsedValueId: valueId,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n }\n\n try {\n const parsed = JSON.parse(jsonString);\n if (!isJsonTypeCompatible(parsed, defaultValue)) {\n return {\n parsedValue: defaultValue,\n parsedValueId: undefined,\n requestedType,\n usedDefault: true,\n reason: \"type-mismatch\",\n };\n }\n return {\n parsedValue: parsed,\n parsedValueId: valueId,\n requestedType,\n usedDefault: false,\n reason: \"found-match\",\n };\n } catch {\n return {\n parsedValue: defaultValue,\n parsedValueId: undefined,\n requestedType,\n usedDefault: true,\n reason: \"invalid-json\",\n };\n }\n};\n\nconst isJsonTypeCompatible = (parsed: unknown, defaultValue: ConfigValueType): boolean => {\n if (parsed === null) {\n return false;\n }\n const defaultType = typeof defaultValue;\n if (defaultType === \"bigint\") {\n return typeof parsed === \"number\";\n }\n return typeof parsed === defaultType;\n};\n","import type { ConfigDirectorLogger, ConfigDirectorLogMessageDecorator, ConfigDirectorLoggingLevel } from \"./types\";\n\nconst LEVELS: Record<ConfigDirectorLoggingLevel, number> = {\n off: -1,\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n};\n\nconst buildDateFormatter = () => {\n const baseOptions: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZoneName: \"short\",\n };\n\n try {\n return new Intl.DateTimeFormat(\n undefined,\n { ...baseOptions, fractionalSecondDigits: 3 } as Intl.DateTimeFormatOptions,\n );\n } catch {\n return new Intl.DateTimeFormat(undefined, baseOptions);\n }\n};\n\nexport class DefaultConsoleLogger implements ConfigDirectorLogger {\n private readonly dateFormatter;\n\n constructor(private readonly level: ConfigDirectorLoggingLevel, private readonly decorator: ConfigDirectorLogMessageDecorator) {\n this.level = level;\n this.decorator = decorator;\n this.dateFormatter = buildDateFormatter();\n }\n\n debug(message: string, ...args: any): void {\n this.log(console.debug, \"debug\", message, ...args);\n }\n\n info(message: string, ...args: any): void {\n this.log(console.info, \"info\", message, ...args);\n }\n\n warn(message: string, ...args: any): void {\n this.log(console.warn, \"warn\", message, ...args);\n }\n\n error(message: string, ...args: any): void {\n this.log(console.error, \"error\", message, ...args);\n }\n\n protected log(loggerFunction: (...args: any) => void, level: ConfigDirectorLoggingLevel, message: string, ...args: any) {\n if (LEVELS[this.level] >= LEVELS[level]) {\n loggerFunction(`[${this.dateFormatter.format(new Date())}] ${this.decorator?.decorateMessage(message)}`, ...args);\n }\n }\n}\n","import { DefaultConsoleLogger } from \"@shared/logger\";\nimport type { ConfigDirectorLoggingLevel, ConfigDirectorLogMessageDecorator } from \"@shared/types\";\n\nclass LogMessageDecorator implements ConfigDirectorLogMessageDecorator {\n decorateMessage(message: string): string {\n return `[ConfigDirector:js-server-sdk] ${message}`;\n }\n}\n\nexport const createDefaultLogger = (\n level?: ConfigDirectorLoggingLevel,\n messageDecorator?: ConfigDirectorLogMessageDecorator,\n) => {\n return new DefaultConsoleLogger(level ?? \"warn\", messageDecorator ?? new LogMessageDecorator());\n};\n","import { ConfigDirectorValidationError } from \"./errors\";\n\nexport interface UrlLike {\n toString(): string;\n}\n\nexport type UrlFactory = (input: string, base?: UrlLike) => UrlLike;\n\nexport const defaultUrlFactory: UrlFactory = (input, base) => new URL(input, base?.toString());\n\nexport const parseUrl = (url: string | undefined): URL | undefined => {\n if (!url) {\n return;\n }\n\n try {\n return new URL(url);\n } catch (error) {\n throw new ConfigDirectorValidationError(`Invalid URL '${url}'. Parsing failed: ${error}`);\n }\n};\n","export type PollingIntervalDefaults = {\n defaultSeconds: number;\n minimumSeconds: number;\n};\n\nexport type ResolvedPollingInterval = {\n seconds: number;\n warning: string | undefined;\n};\n\nexport const resolvePollingInterval = (\n configuredSeconds: number | undefined,\n defaults: PollingIntervalDefaults,\n): ResolvedPollingInterval => {\n if (configuredSeconds === undefined) {\n return { seconds: defaults.defaultSeconds, warning: undefined };\n }\n if (configuredSeconds >= defaults.minimumSeconds) {\n return { seconds: configuredSeconds, warning: undefined };\n }\n return {\n seconds: defaults.minimumSeconds,\n warning: `pollingInterval of ${configuredSeconds} seconds is below the minimum of ${defaults.minimumSeconds} seconds. Using ${defaults.minimumSeconds} seconds.`,\n };\n};\n","import type { ConfigDirectorContext } from \"./types\";\n\nconst CONTEXT_KIND_MAXIMUM_LENGTH = 64;\nconst KIND_SAMPLE_MAXIMUM_CHARACTERS = 128;\n\ntype ContextAdmission =\n | { outcome: \"admitted\"; context: ConfigDirectorContext | undefined }\n | { outcome: \"ignored\"; kindSample: string };\n\nconst isContextKindCharacter = (character: string): boolean =>\n (character >= \"a\" && character <= \"z\") ||\n (character >= \"0\" && character <= \"9\") ||\n character === \"-\" ||\n character === \"_\";\n\nconst isValidContextKind = (kind: string): boolean => {\n if (kind.length < 1 || kind.length > CONTEXT_KIND_MAXIMUM_LENGTH) {\n return false;\n }\n for (const character of kind) {\n if (!isContextKindCharacter(character)) {\n return false;\n }\n }\n return true;\n};\n\nconst isAcceptedKind = (kind: unknown): boolean =>\n kind === undefined || kind === null || (typeof kind === \"string\" && isValidContextKind(kind));\n\nconst stringFormOf = (kind: unknown): string => {\n if (typeof kind === \"string\") {\n return kind;\n }\n try {\n return String(kind);\n } catch {\n return Object.prototype.toString.call(kind);\n }\n};\n\nconst firstCharactersOf = (text: string, maximumCharacters: number): string => {\n let sample = \"\";\n let characterCount = 0;\n for (const character of text) {\n if (characterCount === maximumCharacters) {\n break;\n }\n sample += character;\n characterCount += 1;\n }\n return sample;\n};\n\nexport const admittedContextOrNone = (\n context: ConfigDirectorContext | undefined,\n): ConfigDirectorContext | undefined => {\n const admission = admitContext(context);\n return admission.outcome === \"admitted\" ? admission.context : undefined;\n};\n\nexport const admitContext = (context: ConfigDirectorContext | undefined): ContextAdmission => {\n const kind: unknown = context?.kind;\n if (isAcceptedKind(kind)) {\n return { outcome: \"admitted\", context };\n }\n return {\n outcome: \"ignored\",\n kindSample: firstCharactersOf(stringFormOf(kind), KIND_SAMPLE_MAXIMUM_CHARACTERS),\n };\n};\n","import type { InvalidContextEvent } from \"./telemetry-events\";\nimport type { AggregatedEvent } from \"./types\";\n\nexport const DISTINCT_KIND_SAMPLE_LIMIT = 20;\n\nexport type InvalidContextRecording =\n | \"new-kind-sample\"\n | \"known-kind-sample\"\n | \"first-beyond-sample-limit\"\n | \"beyond-sample-limit\";\n\nexport class InvalidContextSubmissions {\n private readonly countsByKindSample = new Map<string, number>();\n private countBeyondSampleLimit = 0;\n private startTime: Date | undefined;\n\n public record(kindSample: string): InvalidContextRecording {\n this.startTime ??= new Date();\n return this.add(kindSample, 1);\n }\n\n public absorb(entries: AggregatedEvent<InvalidContextEvent>[]): void {\n for (const entry of entries) {\n if (this.startTime === undefined || entry.startTime < this.startTime) {\n this.startTime = entry.startTime;\n }\n this.add(entry.event.kindSample, entry.count);\n }\n }\n\n private add(kindSample: string | undefined, count: number): InvalidContextRecording {\n if (kindSample !== undefined) {\n const knownCount = this.countsByKindSample.get(kindSample);\n if (knownCount !== undefined) {\n this.countsByKindSample.set(kindSample, knownCount + count);\n return \"known-kind-sample\";\n }\n if (this.countsByKindSample.size < DISTINCT_KIND_SAMPLE_LIMIT) {\n this.countsByKindSample.set(kindSample, count);\n return \"new-kind-sample\";\n }\n }\n const firstBeyondSampleLimit = this.countBeyondSampleLimit === 0;\n this.countBeyondSampleLimit += count;\n return firstBeyondSampleLimit ? \"first-beyond-sample-limit\" : \"beyond-sample-limit\";\n }\n\n public takeSnapshot(): AggregatedEvent<InvalidContextEvent>[] {\n const endTime = new Date();\n const startTime = this.startTime ?? endTime;\n const entryOf = (count: number, kindSample?: string): AggregatedEvent<InvalidContextEvent> => ({\n startTime,\n endTime,\n count,\n event: { reason: \"invalid-context-kind\", kindSample },\n });\n const sampledEntries = Array.from(this.countsByKindSample, ([kindSample, count]) =>\n entryOf(count, kindSample),\n );\n const entries =\n this.countBeyondSampleLimit > 0\n ? [...sampledEntries, entryOf(this.countBeyondSampleLimit)]\n : sampledEntries;\n this.clear();\n return entries;\n }\n\n private clear() {\n this.countsByKindSample.clear();\n this.countBeyondSampleLimit = 0;\n this.startTime = undefined;\n }\n}\n","import type { ConfigType, ConfigValueType } from \"../types\";\nimport type { ValueIdGenerator, DroppedEvents } from \"./types\";\n\nconst CONFIG_VALUE_MAX_LENGTH = 500;\n\nexport type TelemetryValue = {\n value?: string;\n valueId?: string;\n type?: ConfigType;\n};\n\nexport type TelemetryEvaluationValue<TV extends ConfigValueType> = {\n value: TV;\n valueId?: string | null | undefined;\n type?: ConfigType;\n};\n\nexport const compactTelemetryValue = async (\n tv: TelemetryValue,\n valueIdGenerator: ValueIdGenerator,\n): Promise<TelemetryValue> => {\n if (tv.valueId) {\n return { valueId: tv.valueId };\n }\n if (tv.value) {\n if (tv.value.length <= CONFIG_VALUE_MAX_LENGTH && tv.type != \"json\") {\n return { value: tv.value };\n }\n\n return { valueId: await valueIdGenerator(tv.value) };\n }\n\n return tv;\n};\n\nexport const mapToTelemetryValue = <TV extends ConfigValueType>(\n ev: TelemetryEvaluationValue<TV>,\n): TelemetryValue => {\n if (ev.type === \"json\" || (ev.type == null && typeof ev.value === \"object\")) {\n if (ev.valueId) {\n return { valueId: ev.valueId, type: \"json\" };\n }\n try {\n const json = JSON.stringify(ev.value);\n return { value: json, type: \"json\" };\n } catch {\n return { value: ev.value.toString(), type: \"json\" };\n }\n }\n\n const stringValue = ev.value?.toString();\n if (stringValue && stringValue.length <= CONFIG_VALUE_MAX_LENGTH) {\n return { value: stringValue };\n }\n\n return ev.valueId ? { valueId: ev.valueId } : { value: stringValue };\n};\n\nexport const djb2Hash = (data: string): string => {\n const hash = djb2(new TextEncoder().encode(data));\n return hash.toString(16).padStart(8, \"0\");\n};\n\nconst djb2 = (bytes: Uint8Array): number => {\n let hash = 5381;\n for (let i = 0; i < bytes.length; i++) {\n hash = (hash << 5) + hash + (bytes[i] ?? 0);\n hash = hash >>> 0;\n }\n return hash;\n};\n\nexport const isEventListEmpty = <T extends Record<string | symbol, any[]>>(eventList: T): boolean => {\n const keys = Object.keys(eventList);\n if (keys.length == 0) {\n return true;\n }\n\n for (const key of keys) {\n if ((eventList[key]?.length ?? 0) > 0) {\n return false;\n }\n }\n\n return true;\n};\n\nexport const isDroppedEventsEmpty = (droppedEvents?: DroppedEvents): boolean => {\n if (!droppedEvents) {\n return true;\n }\n const keys = Object.keys(droppedEvents);\n if (keys.length == 0) {\n return true;\n }\n\n for (const key of keys) {\n if ((droppedEvents[key] ?? 0) > 0) {\n return false;\n }\n }\n\n return true;\n};\n","import { isDroppedEventsEmpty, isEventListEmpty } from \"@shared/telemetry/utils\";\nimport type { ConfigDirectorLogger, ConfigDirectorMetaContext, IdentifyingSdkOptions } from \"../types\";\nimport type { EventReport, EventReporter, EventReporterPayload, ReporterResponse } from \"./types\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\nimport { type UrlLike } from \"@shared/url\";\n\nexport type EventReporterOptions = {\n sdkKey: string;\n sdkIdentity: IdentifyingSdkOptions;\n metaContext: ConfigDirectorMetaContext;\n logger: ConfigDirectorLogger;\n baseUrl: UrlLike;\n};\n\nexport class ServerEventReporter implements EventReporter {\n private readonly sdkKey: string;\n private readonly sdkIdentity: IdentifyingSdkOptions;\n private readonly metaContext: ConfigDirectorMetaContext;\n private readonly logger: ConfigDirectorLogger;\n private readonly url: URL;\n private executeRequests = true;\n\n constructor(options: EventReporterOptions) {\n this.sdkKey = options.sdkKey;\n this.sdkIdentity = options.sdkIdentity;\n this.logger = options.logger;\n this.metaContext = options.metaContext;\n this.url = new URL(\"server/telemetry/v1\", options.baseUrl.toString());\n }\n\n public async report({\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n }: EventReporterPayload): Promise<ReporterResponse> {\n if (!this.executeRequests) {\n return { success: false, fatalError: true };\n }\n\n const eventReport: EventReport = {\n serverSdkKey: this.sdkKey,\n metaContext: {\n sdkName: this.sdkIdentity.sdkName,\n sdkVersion: this.sdkIdentity.sdkVersion,\n appName: this.metaContext.appName,\n appVersion: this.metaContext.appVersion,\n },\n discreteEvents,\n aggregatedEvents,\n droppedEvents,\n };\n if (this.isReportEmpty(eventReport)) {\n return { success: true, fatalError: false };\n }\n\n const response = await this.sendReport(eventReport);\n if (response.fatalError) {\n this.executeRequests = false;\n }\n return response;\n }\n\n private isReportEmpty(eventReport: EventReport) {\n return (\n isEventListEmpty(eventReport.discreteEvents) &&\n isEventListEmpty(eventReport.aggregatedEvents) &&\n isDroppedEventsEmpty(eventReport.droppedEvents)\n );\n }\n\n private async sendReport(eventReport: EventReport): Promise<ReporterResponse> {\n try {\n const response = await fetchWithTimeout(\n 5_000,\n this.url,\n {\n method: \"POST\",\n body: JSON.stringify(eventReport),\n },\n this.logger,\n );\n\n const isFatalError = this.isStatusFatal(response.status);\n if (isFatalError) {\n this.logger.warn(\n `[EventReporter] Received a fatal status response (${response.status}) from the telemetry endpoint. No more telemetry data will be sent.`,\n );\n }\n return { success: response.ok, fatalError: isFatalError };\n } catch (error) {\n this.logger.warn(`[EventReporter] Error attempting to send telemetry data: ${error}`, { error });\n return { success: false, fatalError: false };\n }\n }\n\n private isStatusFatal(status: number | undefined): boolean {\n return !!status && status >= 400 && status < 500 && status != 429;\n }\n}\n","import type { AggregatedEvent, EventQueueSnapshot, ReportableEvent } from \"./types\";\n\nexport class EventAggregator {\n public aggregate<T extends ReportableEvent>(snapshot: EventQueueSnapshot<T>): AggregatedEvent<T>[] {\n if (snapshot.events.length == 0) {\n return [];\n }\n\n const map = new Map<string, { event: T; count: number }>();\n for (const event of snapshot.events) {\n const serializedEvent = JSON.stringify(event);\n const count = map.get(serializedEvent)?.count || 0;\n map.set(serializedEvent, { event, count: count + 1 });\n }\n return Array.from(map).map(([, v]) => {\n return {\n startTime: snapshot.startTime,\n endTime: snapshot.endTime,\n count: v.count,\n event: v.event,\n };\n });\n }\n}\n","import type { EventQueueSnapshot, ReportableEvent } from \"./types\";\n\nexport class EventQueue<T extends ReportableEvent> {\n private readonly limit: number;\n private startTime?: Date;\n private _events: T[] = [];\n private _droppedEventCount = 0;\n\n constructor(limit?: number) {\n this.limit = limit ?? 1_000;\n }\n\n public get events(): T[] {\n return this._events;\n }\n\n public get reachedLimit(): boolean {\n return this._events.length >= this.limit;\n }\n\n public get droppedEventCount(): number {\n return this._droppedEventCount;\n }\n\n public push(...newEvents: T[]) {\n if (!this.startTime) {\n this.startTime = new Date();\n }\n\n const newEventsCountToDrop = Math.max(0, newEvents.length - this.limit);\n const eventsToPush = newEvents.slice(newEventsCountToDrop, newEvents.length);\n this._droppedEventCount += newEventsCountToDrop;\n if (this._events.length + eventsToPush.length > this.limit) {\n const eventCountToDrop = this._events.length + eventsToPush.length - this.limit;\n this._events.splice(0, eventCountToDrop);\n this._droppedEventCount += eventCountToDrop;\n }\n return this._events.push(...eventsToPush);\n }\n\n public takeSnapshot(): EventQueueSnapshot<T> {\n const endTime = new Date();\n const startTime = this.startTime ?? endTime;\n const eventsSnapshot = this._events.splice(0);\n const droppedCount = this._droppedEventCount;\n this.startTime = undefined;\n this._droppedEventCount = 0;\n return {\n startTime,\n endTime,\n events: eventsSnapshot,\n droppedCount,\n };\n }\n\n public clear() {\n this._events = [];\n this.startTime = undefined;\n this._droppedEventCount = 0;\n }\n}\n","import type { ConfigDirectorContext, ConfigDirectorLogger, ConfigDirectorMetaContext, IdentifyingSdkOptions } from \"../types\";\nimport { EventAggregator } from \"./EventAggregator\";\nimport { EventQueue } from \"./EventQueue\";\nimport type { EventReporter, EventSnapshotPreprocessor, ReportableEvent, ReporterResponse, ValueIdGenerator } from \"./types\";\nimport type { UrlFactory, UrlLike } from \"../url\";\n\nexport type TelemetryEventCollectorOptions = {\n sdkKey: string;\n sdkIdentity: IdentifyingSdkOptions;\n metaContext: ConfigDirectorMetaContext;\n logger: ConfigDirectorLogger;\n baseUrl: UrlLike;\n urlFactory: UrlFactory;\n valueIdGenerator: ValueIdGenerator;\n flushIntervalDelay?: number;\n initialFlushIntervalDelay?: number;\n evaluationQueueLimit?: number;\n};\n\nexport abstract class TelemetryEventCollector<T extends ReportableEvent> {\n protected readonly logger: ConfigDirectorLogger;\n protected abstract readonly reporter: EventReporter | undefined;\n protected readonly evaluationEventQueue: EventQueue<T>;\n protected readonly aggregator: EventAggregator = new EventAggregator();\n protected flushIntervalDelay: number;\n protected flushTimeout: ReturnType<typeof setTimeout> | undefined;\n protected collectEvents = true;\n private readonly initialFlushIntervalDelay: number;\n private started = false;\n protected abstract _context?: ConfigDirectorContext;\n protected abstract evaluationEventSnapshotPreprocessor: EventSnapshotPreprocessor<T>;\n\n constructor(options: TelemetryEventCollectorOptions) {\n this.logger = options.logger;\n this.evaluationEventQueue = new EventQueue(options.evaluationQueueLimit ?? 1_000);\n this.flushIntervalDelay = options.flushIntervalDelay ?? 30_000;\n this.initialFlushIntervalDelay = options.initialFlushIntervalDelay ?? 5_000;\n }\n\n public start() {\n if (this.started || !this.collectEvents) {\n return;\n }\n this.started = true;\n this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.initialFlushIntervalDelay);\n }\n\n protected abstract flush(): Promise<ReporterResponse>;\n\n protected async flushAndScheduleNext() {\n if (!this.collectEvents) {\n return;\n }\n let response: ReporterResponse;\n try {\n response = await this.flush();\n } catch (error) {\n this.logger.warn(\"[TelemetryEventCollector] Error flushing telemetry events: \", error);\n response = { success: false, fatalError: false };\n }\n if (response.fatalError) {\n this.collectEvents = false;\n this.close();\n this.logger.warn(\n \"[TelemetryEventCollector] Received a fatal error from telemetry collection. No longer collecting events.\",\n );\n } else if (this.collectEvents) {\n this.cancelScheduledFlush();\n this.flushTimeout = setTimeout(() => this.flushAndScheduleNext(), this.flushIntervalDelay);\n }\n }\n\n protected cancelScheduledFlush() {\n clearTimeout(this.flushTimeout);\n }\n\n public async close() {\n this.collectEvents = false;\n clearTimeout(this.flushTimeout);\n try {\n await this.flush();\n } catch (error) {\n this.logger.warn(\"[TelemetryEventCollector] Error flushing telemetry events during close: \", error);\n }\n this.evaluationEventQueue.clear();\n }\n\n public dispose() {\n this.close();\n }\n}\n","export class LimitedMap<K, V> extends Map<K, V> {\n private readonly limit: number;\n private _droppedCount: number = 0;\n\n constructor(limit: number, entries?: [K, V][]) {\n super(entries);\n this.limit = limit;\n this.enforceLimit();\n }\n\n public override set(key: K, value: V): this {\n super.set(key, value);\n this.enforceLimit();\n return this;\n }\n\n public get droppedCount(): number {\n return this._droppedCount;\n }\n\n public clearAndReset() {\n this.clear();\n this._droppedCount = 0;\n }\n\n private enforceLimit() {\n while (this.size > this.limit) {\n const oldestKey = this.keys().next().value;\n if (oldestKey !== undefined) {\n this.delete(oldestKey);\n this._droppedCount += 1;\n }\n }\n }\n}\n","import type { EvaluatedConfigEvent } from \"./telemetry-events\";\nimport type { EventQueueSnapshot, EventSnapshotPreprocessor, ValueIdGenerator } from \"./types\";\nimport type { TelemetryValue } from \"./utils\";\nimport { compactTelemetryValue } from \"./utils\";\n\nexport class EvaluatedConfigEventPreprocessor implements EventSnapshotPreprocessor<\n EvaluatedConfigEvent<TelemetryValue>\n> {\n constructor(private readonly valueIdGenerator: ValueIdGenerator) {\n this.valueIdGenerator = valueIdGenerator;\n }\n\n async process(\n snapshot: EventQueueSnapshot<EvaluatedConfigEvent<TelemetryValue>>,\n ): Promise<EventQueueSnapshot<EvaluatedConfigEvent<TelemetryValue>>> {\n const events = await Promise.all(\n snapshot.events.map(async (e) => {\n return {\n ...e,\n defaultValue: await compactTelemetryValue(e.defaultValue, this.valueIdGenerator),\n evaluatedValue: await compactTelemetryValue(e.evaluatedValue, this.valueIdGenerator),\n };\n }),\n );\n\n return { ...snapshot, events };\n }\n}\n","import type { ConfigDirectorContext, ConfigValueType } from \"../types\";\nimport { ServerEventReporter } from \"./ServerEventReporter\";\nimport type { EvaluatedConfigEvent } from \"@shared/telemetry/telemetry-events\";\nimport type {\n EvaluatedConfig,\n EventReporter,\n EventSnapshotPreprocessor,\n ReporterResponse,\n ServerTelemetryClient,\n ServerTelemetryEventCollectorOptions,\n} from \"./types\";\nimport { TelemetryEventCollector } from \"@shared/telemetry/TelemetryEventCollector\";\nimport { LimitedMap } from \"@shared/LimitedMap\";\nimport { mapToTelemetryValue, type TelemetryValue } from \"@shared/telemetry/utils\";\nimport { EvaluatedConfigEventPreprocessor } from \"@shared/telemetry/EvaluatedConfigEventPreprocessor\";\nimport { effectiveContextKind } from \"@config-evaluator/context-kind\";\nimport {\n InvalidContextSubmissions,\n type InvalidContextRecording,\n} from \"@shared/telemetry/InvalidContextSubmissions\";\n\nconst DEFAULT_CONTEXTS_LIMIT = 1_000;\n\nexport class ServerTelemetryEventCollector\n extends TelemetryEventCollector<EvaluatedConfigEvent<TelemetryValue>>\n implements ServerTelemetryClient\n{\n protected override evaluationEventSnapshotPreprocessor: EventSnapshotPreprocessor<\n EvaluatedConfigEvent<TelemetryValue>\n >;\n protected readonly reporter: EventReporter;\n protected _context?: ConfigDirectorContext;\n private readonly contexts: LimitedMap<string, ConfigDirectorContext>;\n private readonly invalidContextSubmissions = new InvalidContextSubmissions();\n\n constructor(options: ServerTelemetryEventCollectorOptions) {\n super(options);\n this.reporter = new ServerEventReporter(options);\n this.contexts = new LimitedMap(options.contextLimit ?? DEFAULT_CONTEXTS_LIMIT);\n this.evaluationEventSnapshotPreprocessor = new EvaluatedConfigEventPreprocessor(options.valueIdGenerator);\n }\n\n public evaluatedConfig<T extends ConfigValueType>(payload: EvaluatedConfig<T>): void {\n if (!this.collectEvents) {\n return;\n }\n const context = payload.context ?? undefined;\n const contextKind = effectiveContextKind({ context });\n const contextId = context?.anonymous ? undefined : context?.id;\n if (contextId) {\n this.contexts.set(`${contextKind}:${contextId}`, { ...context, kind: contextKind });\n }\n const event = {\n ...payload.evaluation,\n contextId,\n contextKind: contextId === undefined ? undefined : contextKind,\n };\n\n this.evaluationEventQueue.push(this.sanitizeEvaluatedConfigEvent(event));\n }\n\n public recordInvalidContext(kindSample: string): InvalidContextRecording {\n return this.invalidContextSubmissions.record(kindSample);\n }\n\n private sanitizeEvaluatedConfigEvent<T extends ConfigValueType>(\n event: EvaluatedConfigEvent<T>,\n ): EvaluatedConfigEvent<TelemetryValue> {\n return {\n ...event,\n defaultValue: mapToTelemetryValue({ value: event.defaultValue, type: event.type }),\n evaluatedValue: mapToTelemetryValue({\n value: event.evaluatedValue,\n valueId: event.evaluatedValueId,\n type: event.type,\n }),\n };\n }\n\n private snapshotContexts() {\n const contexts = Array.from(this.contexts.values());\n const droppedCount = this.contexts.droppedCount;\n this.contexts.clearAndReset();\n return { contexts, droppedCount };\n }\n\n protected override async flush(): Promise<ReporterResponse> {\n if (!this.reporter) {\n return { success: false, fatalError: false };\n }\n const evaluationSnapshot = await this.evaluationEventSnapshotPreprocessor.process(\n this.evaluationEventQueue.takeSnapshot(),\n );\n const contextsSnapshot = this.snapshotContexts();\n const response = await this.reporter?.report({\n context: this._context,\n discreteEvents: {\n capturedContexts: contextsSnapshot.contexts,\n },\n aggregatedEvents: {\n evaluatedConfig: this.aggregator.aggregate(evaluationSnapshot),\n invalidContext: this.invalidContextSubmissions.takeSnapshot(),\n },\n droppedEvents: {\n evaluatedConfig: evaluationSnapshot.droppedCount,\n capturedContexts: contextsSnapshot.droppedCount,\n },\n });\n return response;\n }\n}\n","import type { ServerTelemetryClient } from \"./types\";\n\nexport const discardingTelemetryClient: ServerTelemetryClient = {\n start: () => {},\n evaluatedConfig: () => {},\n recordInvalidContext: () => \"new-kind-sample\",\n close: async () => {},\n};\n","const BASE62 = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\nexport const toBase62 = (buf: Uint8Array, length: number): string => {\n const ZERO = BigInt(0);\n const BASE = BigInt(62);\n const hex = Array.from(buf)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n let n = BigInt(\"0x\" + hex);\n let result = \"\";\n while (n > ZERO) {\n result = BASE62[Number(n % BASE)] + result;\n n /= BASE;\n }\n return result.padStart(length, \"0\");\n};\n","export const DIGEST_BYTES = 16;\nexport const BASE62_LENGTH = Math.ceil((DIGEST_BYTES * 8) / Math.log2(62));\n","import { createHash } from \"node:crypto\";\nimport { toBase62 } from \"@shared/base62-encoder\";\nimport { DIGEST_BYTES, BASE62_LENGTH } from \"@shared/value-id\";\n\nexport const generateValueId = async (v: string | number | boolean | null | undefined): Promise<string> => {\n const hash = createHash(\"sha256\")\n .update(v?.toString() ?? \"\")\n .digest();\n return toBase62(hash.subarray(0, DIGEST_BYTES), BASE62_LENGTH);\n};\n","import { StreamingTransport } from \"./StreamingTransport\";\nimport { PollingTransport } from \"./PollingTransport\";\nimport { OfflineTransport } from \"./OfflineTransport\";\nimport { planConnection, type ConnectionPlan, type LiveConnectionMode } from \"./connection-plan\";\nimport { ConfigEvaluator } from \"@config-evaluator/ConfigEvaluator\";\nimport { getRequestedType, parseConfigValue } from \"@shared/value-parser\";\nimport type {\n ConfigDirectorContext,\n ConfigDirectorClientOptions,\n ConfigDirectorClient,\n ClientEvents,\n WatchHandler,\n ConfigValueType,\n ConfigDirectorLogger,\n Transport,\n IdentifyingSdkOptions,\n ConfigBundle,\n ConfigDefinition,\n ConfigState,\n ConfigEvaluation,\n HookHandler,\n InternalClientOptions,\n OfflineClientOptions,\n} from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\nimport { ConfigDirectorInitializationError, ConfigDirectorValidationError } from \"@shared/errors\";\nimport EventEmitter from \"node:events\";\nimport type { ConfigDirectorMetaContext, EvaluationReason } from \"@shared/types\";\nimport { defaultUrlFactory } from \"@shared/url\";\nimport {\n SERVER_DEFAULT_POLLING_INTERVAL_SECONDS,\n SERVER_MIN_POLLING_INTERVAL_SECONDS,\n} from \"@shared/constants\";\nimport { resolvePollingInterval } from \"@shared/transport/pollingInterval\";\nimport { admitContext } from \"@shared/context-kind\";\nimport {\n DISTINCT_KIND_SAMPLE_LIMIT,\n type InvalidContextRecording,\n} from \"@shared/telemetry/InvalidContextSubmissions\";\nimport { ServerTelemetryEventCollector, type ServerTelemetryClient } from \"./telemetry\";\nimport { discardingTelemetryClient } from \"./telemetry/discarding\";\nimport { generateValueId } from \"./telemetry/value-id-generator\";\n\nconst DEFAULT_FLUSH_INTERVAL = 30_000;\nconst DEFAULT_EVENT_QUEUE_LIMIT = 5_000;\n\nconst liveTimeoutWarningDetails: { [M in LiveConnectionMode]: string } = {\n streaming:\n \"The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.\",\n polling:\n \"Since the client was configured without streaming, configs may not update and always return the default value.\",\n};\n\ntype WatchHandlerWithOptions<T extends ConfigValueType> = {\n handler: WatchHandler<T>;\n defaultValue: T;\n requestedType: string;\n context?: ConfigDirectorContext;\n};\n\nconst logInvalidContext: {\n [R in InvalidContextRecording]: (logger: ConfigDirectorLogger, kindSample: string) => void;\n} = {\n \"new-kind-sample\": (logger, kindSample) =>\n logger.error(\n `[ConfigDirectorClient] Ignored a context with the invalid kind '${kindSample}' and evaluated as if no context was supplied. A context kind has 1 to 64 characters, using only lowercase letters, digits, '-' and '_'.`,\n ),\n \"known-kind-sample\": () => {},\n \"first-beyond-sample-limit\": (logger) =>\n logger.error(\n `[ConfigDirectorClient] Ignored a context with another invalid kind and evaluated as if no context was supplied. ${DISTINCT_KIND_SAMPLE_LIMIT} different invalid kinds were logged recently, so further invalid kinds are not logged individually.`,\n ),\n \"beyond-sample-limit\": () => {},\n};\n\nconst usesAnySegment = (definition: ConfigDefinition, segmentIds: Set<string>): boolean =>\n (definition.target?.rules ?? []).some(\n (rule) =>\n rule.type === \"conditional\" &&\n (rule.conditions ?? []).some(\n (condition) => condition.kind === \"segment\" && segmentIds.has(condition.segmentId),\n ),\n );\n\nexport class DefaultConfigDirectorClient implements ConfigDirectorClient {\n private logger: ConfigDirectorLogger;\n private usageEventCollector: ServerTelemetryClient;\n private configSet: ConfigBundle | undefined;\n private handlersMap: Map<string, WatchHandlerWithOptions<any>[]> = new Map();\n private transport: Transport;\n private eventEmitter = new EventEmitter();\n private timeout: number;\n private ready = false;\n private readyPromise: Promise<void> | undefined;\n private readyResolve: ((cancelled: boolean) => void) | undefined;\n private readyTimer: ReturnType<typeof setTimeout> | undefined;\n private readonly connectionPlan: ConnectionPlan;\n private configEvaluator: ConfigEvaluator;\n private metaContext: ConfigDirectorMetaContext;\n\n public static from(\n serverSdkKeyOrOptions: string | OfflineClientOptions,\n sdkOptions: IdentifyingSdkOptions,\n clientOptions: ConfigDirectorClientOptions | undefined,\n ): DefaultConfigDirectorClient {\n if (typeof serverSdkKeyOrOptions === \"object\") {\n return new DefaultConfigDirectorClient(undefined, sdkOptions, serverSdkKeyOrOptions);\n }\n return new DefaultConfigDirectorClient(serverSdkKeyOrOptions, sdkOptions, clientOptions);\n }\n\n constructor(\n serverSdkKey: string | undefined,\n sdkOptions: IdentifyingSdkOptions,\n clientOptions?: ConfigDirectorClientOptions,\n internalClientOptions?: InternalClientOptions,\n ) {\n this.logger = clientOptions?.logger ?? createDefaultLogger();\n this.timeout = clientOptions?.connection?.timeout ?? 3_000;\n this.metaContext = clientOptions?.metadata ?? {};\n this.connectionPlan = planConnection(serverSdkKey, clientOptions?.connection);\n this.configEvaluator = new ConfigEvaluator(this.logger);\n this.usageEventCollector =\n internalClientOptions?.telemetry ?? this.buildTelemetryClient(sdkOptions, clientOptions);\n this.transport = internalClientOptions?.transport ?? this.buildTransport(sdkOptions, clientOptions);\n\n this.transport.on(\"configBundleReceived\", (configBundle: ConfigBundle) => {\n this.readyResolve?.(false);\n const removedKeys = this.findKeysRemovedBy(configBundle);\n if (!this.configSet || configBundle.kind == \"full\") {\n this.configSet = configBundle;\n } else {\n this.configSet.configs = {\n ...this.configSet.configs,\n ...configBundle.configs,\n };\n this.configSet.segments = {\n ...this.configSet.segments,\n ...configBundle.segments,\n };\n }\n const keys = this.findKeysUpdatedBy(configBundle);\n this.eventEmitter.emit(\"configsUpdated\", { keys, removedKeys });\n this.updateWatchers([...keys, ...removedKeys]);\n this.logger.debug(\n `[ConfigDirectorClient] ConfigSet updated from server with ${keys.length} key(s) and ${removedKeys.length} removed key(s)`,\n { keys, removedKeys },\n );\n });\n\n this.transport.on(\"connectionError\", (error: Error) => {\n this.logger.error(\n \"[ConfigDirectorClient] The connection encountered an unrecoverable error and will not be retried. Configs will no longer receive updates.\",\n error,\n );\n this.eventEmitter.emit(\"connectionError\", { error });\n });\n\n this.registerHandler(\"clientReady\", clientOptions?.hooks?.clientReady);\n this.registerHandler(\"configEvaluated\", clientOptions?.hooks?.configEvaluated);\n this.registerHandler(\"configsUpdated\", clientOptions?.hooks?.configsUpdated);\n this.registerHandler(\"connectionError\", clientOptions?.hooks?.connectionError);\n }\n\n private registerHandler<T extends keyof ClientEvents>(\n event: T,\n handler: HookHandler<T> | HookHandler<T>[] | undefined,\n ) {\n if (!handler) {\n return;\n }\n\n const handlers = Array.isArray(handler) ? handler : [handler];\n for (const h of handlers) {\n if (typeof h === \"function\") {\n this.on(event, h);\n }\n }\n }\n\n public async initialize() {\n try {\n this.ready = false;\n this.readyPromise = new Promise<boolean>((resolve) => {\n this.readyResolve = resolve;\n }).then((cancelled) => {\n if (cancelled) {\n return;\n }\n this.ready = true;\n this.eventEmitter.emit(\"clientReady\");\n this.logger.debug(\"[ConfigDirectorClient] Received initial payload from the server, client is ready\");\n });\n this.usageEventCollector.start();\n const startTime = new Date().getTime();\n await this.transport.connect(this.timeout);\n const elapsedTime = new Date().getTime() - startTime;\n const remainingTimeout = this.timeout - elapsedTime;\n if (remainingTimeout > 0) {\n await Promise.race([\n this.readyPromise,\n new Promise<void>((resolve) => {\n this.readyTimer = setTimeout(() => resolve(), remainingTimeout);\n }),\n ]);\n this.clearReadyTimer();\n }\n if (!this.ready) {\n this.logger.warn(\n `[ConfigDirectorClient] Timed out waiting for initialization after ${this.timeout}ms. ${this.timeoutWarningDetails()}`,\n );\n }\n } catch (error) {\n if (error instanceof ConfigDirectorInitializationError) {\n throw error;\n }\n this.logger.error(\"[ConfigDirectorClient] An error occurred during initialization: \", error);\n }\n }\n\n private timeoutWarningDetails(): string {\n switch (this.connectionPlan.kind) {\n case \"connect\":\n return liveTimeoutWarningDetails[this.connectionPlan.mode];\n case \"stay-offline\":\n return \"The offline values were not delivered, so configs return the default value.\";\n }\n }\n\n private pollingIntervalFor(\n mode: LiveConnectionMode,\n configuredSeconds: number | undefined,\n ): number | undefined {\n if (mode !== \"polling\") {\n return undefined;\n }\n const resolved = resolvePollingInterval(configuredSeconds, {\n defaultSeconds: SERVER_DEFAULT_POLLING_INTERVAL_SECONDS,\n minimumSeconds: SERVER_MIN_POLLING_INTERVAL_SECONDS,\n });\n if (resolved.warning) {\n this.logger.warn(`[ConfigDirectorClient] ${resolved.warning}`);\n }\n return resolved.seconds;\n }\n\n private buildTelemetryClient(\n sdkOptions: IdentifyingSdkOptions,\n clientOptions: ConfigDirectorClientOptions | undefined,\n ): ServerTelemetryClient {\n const plan = this.connectionPlan;\n switch (plan.kind) {\n case \"stay-offline\":\n return discardingTelemetryClient;\n case \"connect\": {\n const queueLimit = clientOptions?.telemetry?.eventQueueLimit ?? DEFAULT_EVENT_QUEUE_LIMIT;\n return new ServerTelemetryEventCollector({\n sdkKey: plan.serverSdkKey,\n sdkIdentity: sdkOptions,\n metaContext: clientOptions?.metadata ?? {},\n logger: this.logger,\n baseUrl: plan.baseUrl,\n flushIntervalDelay: clientOptions?.telemetry?.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n evaluationQueueLimit: Math.ceil(queueLimit * 0.7),\n contextLimit: Math.floor(queueLimit * 0.3),\n urlFactory: defaultUrlFactory,\n valueIdGenerator: generateValueId,\n });\n }\n }\n }\n\n private buildTransport(\n sdkOptions: IdentifyingSdkOptions,\n clientOptions: ConfigDirectorClientOptions | undefined,\n ): Transport {\n const plan = this.connectionPlan;\n switch (plan.kind) {\n case \"stay-offline\":\n return new OfflineTransport(plan.source);\n case \"connect\": {\n const transportConstructor = this.getTransportConstructor(plan.mode);\n return new transportConstructor({\n serverSdkKey: plan.serverSdkKey,\n baseUrl: plan.baseUrl,\n metaContext: {\n ...clientOptions?.metadata,\n sdkName: sdkOptions.sdkName,\n sdkVersion: sdkOptions.sdkVersion,\n },\n logger: this.logger,\n pollingInterval: this.pollingIntervalFor(plan.mode, clientOptions?.connection?.pollingInterval),\n });\n }\n }\n }\n\n private getTransportConstructor(mode: LiveConnectionMode) {\n switch (mode) {\n case \"polling\":\n return PollingTransport;\n case \"streaming\":\n return StreamingTransport;\n }\n }\n\n private findKeysRemovedBy(configBundle: ConfigBundle): string[] {\n if (!this.configSet || configBundle.kind != \"full\") {\n return [];\n }\n return Object.keys(this.configSet.configs).filter(\n (key) => !Object.prototype.hasOwnProperty.call(configBundle.configs, key),\n );\n }\n\n private findKeysUpdatedBy(configBundle: ConfigBundle): string[] {\n const carriedSegmentIds = new Set(Object.keys(configBundle.segments ?? {}));\n const keysUsingCarriedSegments = Object.entries(this.configSet?.configs ?? {})\n .filter(([, definition]) => usesAnySegment(definition, carriedSegmentIds))\n .map(([key]) => key);\n return [...new Set([...Object.keys(configBundle.configs), ...keysUsingCarriedSegments])];\n }\n\n private updateWatchers(configKeys: string[]) {\n configKeys.forEach((configKey) => this.updateWatchersForConfig(configKey));\n }\n\n private updateWatchersForConfig(configKey: string) {\n const configDefinition = this.configSet?.configs[configKey];\n this.handlersMap.get(configKey)?.forEach((h) => {\n const value = this.getValueFromConfigDefinition(configKey, configDefinition, h.defaultValue, h.context);\n h.handler(value);\n });\n }\n\n public watch<T extends ConfigValueType>(\n configKey: string,\n defaultValue: T,\n callback: WatchHandler<T>,\n context?: ConfigDirectorContext,\n ) {\n this.validateDefaultValue(defaultValue);\n\n const handlers = this.handlersMap.get(configKey);\n const handlerWithOptions = {\n handler: callback,\n defaultValue,\n requestedType: typeof defaultValue,\n context,\n };\n if (handlers) {\n handlers.push(handlerWithOptions);\n } else {\n this.handlersMap.set(configKey, [handlerWithOptions]);\n }\n\n return () => this.unwatch(configKey, callback);\n }\n\n public unwatch<T extends ConfigValueType>(configKey: string, callback?: WatchHandler<T>) {\n const handlers = this.handlersMap.get(configKey);\n if (!handlers) {\n return;\n }\n\n if (callback) {\n const index = handlers.findIndex((h) => h.handler == callback);\n if (index >= 0) {\n handlers?.splice(index, 1);\n }\n } else {\n handlers.splice(0);\n }\n }\n\n public getValue<T extends ConfigValueType>(\n configKey: string,\n defaultValue: T,\n context?: ConfigDirectorContext,\n ): T {\n this.validateDefaultValue(defaultValue);\n\n const configDefinition = this.configSet?.configs[configKey];\n return this.getValueFromConfigDefinition(configKey, configDefinition, defaultValue, context);\n }\n\n private getValueFromConfigDefinition<T extends ConfigValueType>(\n configKey: string,\n configDefinition: ConfigDefinition | undefined,\n defaultValue: T,\n suppliedContext?: ConfigDirectorContext,\n ): T {\n const context = this.contextToEvaluate(suppliedContext);\n if (configDefinition) {\n const configState = this.configEvaluator.evaluate(\n configDefinition,\n { context, metadata: this.metaContext },\n this.configSet?.segments,\n );\n const parseResult = parseConfigValue<T>(configState, defaultValue);\n this.usageEventCollector.evaluatedConfig({\n context,\n evaluation: {\n key: configKey,\n type: configState.type,\n defaultValue: defaultValue,\n requestedType: parseResult.requestedType,\n evaluatedValue: parseResult.parsedValue,\n evaluatedValueId: parseResult.parsedValueId,\n usedDefault: parseResult.usedDefault,\n evaluationReason: parseResult.reason,\n },\n });\n this.dispatchEvaluationEvent({\n key: configKey,\n value: parseResult.parsedValue,\n valueId: parseResult.parsedValueId ?? undefined,\n isDefaultValue: parseResult.usedDefault,\n reason: parseResult.reason,\n context: context,\n });\n this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);\n return parseResult.parsedValue;\n } else {\n this.logger.debug(\n `[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`,\n );\n const reason: EvaluationReason = this.isReady ? \"config-state-missing\" : \"client-not-ready\";\n this.usageEventCollector.evaluatedConfig({\n context,\n evaluation: {\n key: configKey,\n defaultValue: defaultValue,\n requestedType: getRequestedType(defaultValue),\n evaluatedValue: defaultValue,\n usedDefault: true,\n evaluationReason: reason,\n },\n });\n this.dispatchEvaluationEvent({\n key: configKey,\n value: defaultValue,\n isDefaultValue: true,\n reason,\n context: context,\n });\n return defaultValue;\n }\n }\n\n private contextToEvaluate(\n suppliedContext: ConfigDirectorContext | undefined,\n ): ConfigDirectorContext | undefined {\n const admission = admitContext(suppliedContext);\n switch (admission.outcome) {\n case \"admitted\":\n return admission.context;\n case \"ignored\": {\n const recording = this.usageEventCollector.recordInvalidContext(admission.kindSample);\n logInvalidContext[recording](this.logger, admission.kindSample);\n return undefined;\n }\n }\n }\n\n private dispatchEvaluationEvent(event: ConfigEvaluation) {\n setTimeout(() => this.eventEmitter.emit(\"configEvaluated\", { evaluation: event }), 0);\n }\n\n private validateDefaultValue<T extends ConfigValueType>(defaultValue: T) {\n if (defaultValue === undefined || defaultValue === null) {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config must be defined and non-null.\",\n );\n }\n\n if (typeof defaultValue === \"function\") {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config cannot be a function.\",\n );\n }\n }\n\n public get isReady(): boolean {\n return this.ready;\n }\n\n public on<T extends keyof ClientEvents>(eventName: T, handler: (args: ClientEvents[T]) => void): void {\n this.eventEmitter.on(eventName, handler);\n }\n\n public off<T extends keyof ClientEvents>(eventName: T, handler?: (args: ClientEvents[T]) => void) {\n if (handler) {\n this.eventEmitter.off(eventName, handler);\n } else {\n this.eventEmitter.removeAllListeners(eventName);\n }\n }\n\n public removeAllObservers() {\n this.logger.debug(\"[ConfigDirectorClient] removeAllObservers() has been called, removing all observers\");\n this.eventEmitter.removeAllListeners();\n this.handlersMap.clear();\n }\n\n public unwatchAll() {\n this.handlersMap.clear();\n }\n\n public getAllConfigs(options?: {\n context?: ConfigDirectorContext;\n configKeys?: string[];\n }): Record<string, ConfigState> {\n const context = this.contextToEvaluate(options?.context);\n if (!this.configSet) return {};\n\n const entries = Object.entries(this.configSet.configs);\n const filtered = options?.configKeys\n ? entries.filter(([key]) => options.configKeys?.includes(key))\n : entries;\n\n return Object.fromEntries(\n filtered.map(([key, config]) => [\n key,\n this.configEvaluator.evaluate(\n config,\n { context, metadata: this.metaContext },\n this.configSet?.segments,\n ),\n ]),\n );\n }\n\n public closeConnection() {\n this.logger.debug(\n \"[ConfigDirectorClient] closeConnection() has been called, closing connection to server\",\n );\n this.transport.close();\n this.ready = false;\n }\n\n private clearReadyTimer() {\n clearTimeout(this.readyTimer);\n this.readyTimer = undefined;\n }\n\n public dispose() {\n this.removeAllObservers();\n this.closeConnection();\n this.readyResolve?.(true);\n this.clearReadyTimer();\n void this.usageEventCollector.close().catch((error) => {\n this.logger.warn(\"[ConfigDirectorClient] Error flushing telemetry while disposing the client: \", error);\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./DefaultConfigDirectorClient-B1vsd-g7.cjs`);let t=require(`node:events`);t=e.a(t);const n=(t,n)=>{if(t.trim().length==0)throw new e.i(`Invalid config key. The key of a test value cannot be empty.`);return r(t,n)},r=(t,n)=>{switch(typeof n){case`boolean`:return{type:`boolean`,value:n?`true`:`false`};case`number`:return i(t,n);case`string`:return{type:`string`,value:n};case`object`:if(n!==null)return o(t,n,``),{type:`json`,value:JSON.stringify(n)}}throw new e.i(`Invalid test value for '${t}': ${l(n)}. Use a boolean, a number, a string, a plain object, or an array.`)},i=(t,n)=>{if(!Number.isFinite(n))throw new e.i(`Invalid test value for '${t}': ${l(n)} is not a finite number.`);if(Number.isInteger(n)){if(!Number.isSafeInteger(n))throw new e.i(`Invalid test value for '${t}': ${n} is not a safe integer, so the SDK could not read it back exactly.`);return{type:`integer`,value:String(n)}}return{type:`float`,value:a(n)}},a=e=>{let t=String(e),n=t.indexOf(`e-`);if(n<0)return t;let r=t.startsWith(`-`)?`-`:``,[i=``,a=``]=t.slice(r.length,n).split(`.`),o=Number(t.slice(n+2))-i.length;return`${r}0.${`0`.repeat(o)}${i}${a}`},o=(e,t,n)=>{if(!(t===null||typeof t==`boolean`||typeof t==`string`)){if(typeof t==`number`){if(!Number.isFinite(t))throw s(e,n,`${l(t)} is not a finite number`);return}if(Array.isArray(t)){t.forEach((t,r)=>o(e,t,`${n}[${r}]`));return}if(c(t)){for(let[r,i]of Object.entries(t))o(e,i,n?`${n}.${r}`:r);return}throw s(e,n,`${l(t)} cannot be encoded as JSON`)}},s=(t,n,r)=>new e.i(`Invalid test value for '${t}'${n?` at '${n}'`:``}: ${r}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`),c=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},l=e=>e===null?`null`:typeof e==`function`?`a function`:typeof e==`object`?`a ${e.constructor?.name??`object`} instance`:typeof e==`symbol`?`a symbol`:String(e),u=(e,t)=>{let r=n(e,t);return{id:`test-config:${e}`,key:e,type:r.type,variations:[{name:null,value:r.value}],target:{defaultValue:r.value,rules:[]}}},d={environmentId:`test-environment`,projectId:`test-project`};var f=class{constructor(e,n){this.logger=e,this.emitter=new t.default,this.configs=new Map,this.pendingDeliveries=[],this.armed=`nothing`,this.connected=!1,this.delivering=!1,this.encodeAll(n).forEach((e,t)=>this.configs.set(t,e))}setValue(e,t){let n=u(e,t);this.configs.set(e,n),this.deliverIfConnected({...d,kind:`delta`,configs:{[e]:n}})}removeValue(e){this.configs.delete(e),this.deliverIfConnected(this.fullUpdate())}replaceValues(e){let t=this.encodeAll(e);this.configs.clear(),t.forEach((e,t)=>this.configs.set(t,e)),this.armed=`nothing`,this.deliverIfConnected(this.fullUpdate())}hold(){this.armed=`hold`}complete(){this.pendingHold?this.pendingHold.complete():this.armed===`hold`&&(this.armed=`nothing`)}fail(){this.pendingHold?this.pendingHold.fail():this.armed=`failure`}connect(e){this.endAttempt();let t=this.armed;switch(this.armed=`nothing`,t){case`failure`:return this.failNow();case`hold`:return this.holdNow(e);default:return this.connectNow(),Promise.resolve(this)}}get isConnected(){return this.connected}on(e,t){this.emitter.on(e,t)}off(e,t){t?this.emitter.off(e,t):this.emitter.removeAllListeners(e)}close(){this.endAttempt()}dispose(){this.close(),this.emitter.removeAllListeners()}endAttempt(){this.connected=!1,this.pendingHold?.end()}connectNow(){this.connected=!0,this.deliver(this.fullUpdate())}holdNow(e){return new Promise((t,n)=>{let r=setTimeout(()=>this.pendingHold?.end(),e),i=()=>{clearTimeout(r),this.pendingHold=void 0};this.pendingHold={end:()=>{i(),t(this)},complete:()=>{i(),this.connectNow(),t(this)},fail:()=>{i();let e=this.fatalError();this.emitter.emit(`connectionError`,e),n(e)}}})}failNow(){let e=this.fatalError();return this.emitter.emit(`connectionError`,e),Promise.reject(e)}fatalError(){return new e.r(`Connection failed with status: 401. Error: the test client failed this initialization. This is an unrecoverable error, will not attempt to reconnect.`,401)}deliverIfConnected(e){this.connected&&this.deliver(e)}deliver(e){if(this.pendingDeliveries.push(e),!this.delivering){this.delivering=!0;try{let e=this.pendingDeliveries.shift();for(;e;)this.emitCaught(e),e=this.pendingDeliveries.shift()}finally{this.delivering=!1}}}emitCaught(e){try{this.emitter.emit(`configBundleReceived`,e)}catch(e){this.logger.error(`[InMemoryConnection] Error dispatching a config update from the test client: `,e)}}fullUpdate(){return{...d,kind:`full`,configs:Object.fromEntries(this.configs)}}encodeAll(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,u(n,r));return t}};const p={start:()=>{},evaluatedConfig:()=>{},close:async()=>{}};var m=class{constructor(e,t){this.client=e,this.connection=t}setValue(e,t){this.connection.setValue(e,t)}removeValue(e){this.connection.removeValue(e)}replaceValues(e){this.connection.replaceValues(e)}holdInitialization(){this.connection.hold()}completeInitialization(){this.connection.complete()}failInitialization(){this.connection.fail()}};const h=(t,n)=>{let r=n?.logger??e.n(),i=new f(r,n?.values??{});return new m(new e.t(`test-client`,t,{logger:r,connection:{timeout:n?.timeout}},{transport:i,telemetry:p}),i)},g=e=>h({sdkName:`js-server-sdk`,sdkVersion:`1.9.1`},e);exports.ConfigDirectorConnectionError=e.r,exports.ConfigDirectorValidationError=e.i,exports.createTestClient=g;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./DefaultConfigDirectorClient-BoqR23zq.cjs`);let t=require(`node:events`);t=e.c(t);const n={environmentId:`test-environment`,projectId:`test-project`};var r=class{constructor(e,n){this.logger=e,this.emitter=new t.default,this.configs=new Map,this.pendingDeliveries=[],this.armed=`nothing`,this.connected=!1,this.delivering=!1,this.encodeAll(n).forEach((e,t)=>this.configs.set(t,e))}setValue(e,t){let r=this.encode(e,t);this.configs.set(e,r),this.deliverIfConnected({...n,kind:`delta`,configs:{[e]:r}})}removeValue(e){this.configs.delete(e),this.deliverIfConnected(this.fullUpdate())}replaceValues(e){let t=this.encodeAll(e);this.configs.clear(),t.forEach((e,t)=>this.configs.set(t,e)),this.armed=`nothing`,this.deliverIfConnected(this.fullUpdate())}hold(){this.armed=`hold`}complete(){this.pendingHold?this.pendingHold.complete():this.armed===`hold`&&(this.armed=`nothing`)}fail(){this.pendingHold?this.pendingHold.fail():this.armed=`failure`}connect(e){this.endAttempt();let t=this.armed;switch(this.armed=`nothing`,t){case`failure`:return this.failNow();case`hold`:return this.holdNow(e);default:return this.connectNow(),Promise.resolve(this)}}get isConnected(){return this.connected}on(e,t){this.emitter.on(e,t)}off(e,t){t?this.emitter.off(e,t):this.emitter.removeAllListeners(e)}close(){this.endAttempt()}dispose(){this.close(),this.emitter.removeAllListeners()}endAttempt(){this.connected=!1,this.pendingHold?.end()}connectNow(){this.connected=!0,this.deliver(this.fullUpdate())}holdNow(e){return new Promise((t,n)=>{let r=setTimeout(()=>this.pendingHold?.end(),e),i=()=>{clearTimeout(r),this.pendingHold=void 0};this.pendingHold={end:()=>{i(),t(this)},complete:()=>{i(),this.connectNow(),t(this)},fail:()=>{i();let e=this.fatalError();this.emitter.emit(`connectionError`,e),n(e)}}})}failNow(){let e=this.fatalError();return this.emitter.emit(`connectionError`,e),Promise.reject(e)}fatalError(){return new e.a(`Connection failed with status: 401. Error: the test client failed this initialization. This is an unrecoverable error, will not attempt to reconnect.`,401)}deliverIfConnected(e){this.connected&&this.deliver(e)}deliver(e){if(this.pendingDeliveries.push(e),!this.delivering){this.delivering=!0;try{let e=this.pendingDeliveries.shift();for(;e;)this.emitCaught(e),e=this.pendingDeliveries.shift()}finally{this.delivering=!1}}}emitCaught(e){try{this.emitter.emit(`configBundleReceived`,e)}catch(e){this.logger.error(`[InMemoryConnection] Error dispatching a config update from the test client: `,e)}}fullUpdate(){return{...n,kind:`full`,configs:Object.fromEntries(this.configs)}}encodeAll(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,this.encode(n,r));return t}encode(t,n){return e.i(`test-config:${t}`,t,n)}},i=class{constructor(e,t){this.client=e,this.connection=t}setValue(e,t){this.connection.setValue(e,t)}removeValue(e){this.connection.removeValue(e)}replaceValues(e){this.connection.replaceValues(e)}holdInitialization(){this.connection.hold()}completeInitialization(){this.connection.complete()}failInitialization(){this.connection.fail()}};const a=(t,n)=>{let a=n?.logger??e.r(),o=new r(a,n?.values??{});return new i(new e.t(`test-client`,t,{logger:a,connection:{timeout:n?.timeout}},{transport:o,telemetry:e.n}),o)},o=e=>a({sdkName:`js-server-sdk`,sdkVersion:`1.11.0`},e);exports.ConfigDirectorConnectionError=e.a,exports.ConfigDirectorValidationError=e.s,exports.createTestClient=o;
@@ -1,14 +1,5 @@
1
- import { a as ConfigDirectorLogger, t as ConfigDirectorClient } from "./types-CcUNxeqN.cjs";
1
+ import { d as NativeValues, i as ConfigDirectorClient, l as NativeJsonValue, m as ConfigDirectorLogger, r as ConfigDirectorValidationError, t as ConfigDirectorConnectionError, u as NativeValue } from "./errors-C7n65Oc5.cjs";
2
2
 
3
- //#region ../shared/src/testing/values.d.ts
4
- type TestJsonValue = boolean | number | string | null | TestJsonValue[] | {
5
- [key: string]: TestJsonValue;
6
- };
7
- type TestValue = boolean | number | string | TestJsonValue[] | {
8
- [key: string]: TestJsonValue;
9
- };
10
- type TestValues = Record<string, TestValue>;
11
- //#endregion
12
3
  //#region src/testing/TestClient.d.ts
13
4
  /**
14
5
  * Options for {@link createTestClient}. Every option is optional.
@@ -20,14 +11,16 @@ type TestClientOptions = {
20
11
  * number (`float`), a string, or a plain object or array (`json`). Every context receives the
21
12
  * same value.
22
13
  */
23
- values?: TestValues;
14
+ values?: NativeValues;
24
15
  /**
25
16
  * The connection timeout of the SDK client, in milliseconds. It bounds how long a held
26
17
  * `initialize` waits. Defaults to the SDK's production timeout.
27
18
  */
28
19
  timeout?: number;
29
20
  /**
30
- * The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level.
21
+ * The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level. Since
22
+ * the test client sends no telemetry, every read that supplies a context with an invalid kind
23
+ * logs an error, rather than each invalid kind once per telemetry interval.
31
24
  */
32
25
  logger?: ConfigDirectorLogger;
33
26
  };
@@ -49,7 +42,7 @@ interface TestClient {
49
42
  * Stores `value` under `key` and, when the client is connected, delivers it as an update, so
50
43
  * reads, watchers, and `configsUpdated` handlers see it.
51
44
  */
52
- setValue(key: string, value: TestValue): void;
45
+ setValue(key: string, value: NativeValue): void;
53
46
  /**
54
47
  * Removes `key` and, when the client is connected, delivers a full update without it, so reads
55
48
  * fall back to the in-code default value and watchers of `key` receive that default.
@@ -59,7 +52,7 @@ interface TestClient {
59
52
  * Replaces every stored value with `values`, disarms any armed hold or failure, and, when the
60
53
  * client is connected, delivers the new values as a full update.
61
54
  */
62
- replaceValues(values: TestValues): void;
55
+ replaceValues(values: NativeValues): void;
63
56
  /**
64
57
  * Makes the next `initialize` wait until {@link completeInitialization} or
65
58
  * {@link failInitialization} is called, or until the client's timeout elapses.
@@ -77,17 +70,6 @@ interface TestClient {
77
70
  failInitialization(): void;
78
71
  }
79
72
  //#endregion
80
- //#region ../shared/src/errors.d.ts
81
- declare class ConfigDirectorConnectionError extends Error {
82
- readonly name: string;
83
- readonly status?: number;
84
- constructor(message: string, status?: number);
85
- }
86
- declare class ConfigDirectorValidationError extends Error {
87
- readonly name: string;
88
- constructor(message: string);
89
- }
90
- //#endregion
91
73
  //#region src/testing.d.ts
92
74
  /**
93
75
  * Creates a {@link TestClient}: a real `ConfigDirectorClient` connected to an in-memory server
@@ -107,5 +89,5 @@ declare class ConfigDirectorValidationError extends Error {
107
89
  */
108
90
  declare const createTestClient: (options?: TestClientOptions) => TestClient;
109
91
  //#endregion
110
- export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type TestJsonValue, type TestValue, type TestValues, createTestClient };
92
+ export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type NativeJsonValue as TestJsonValue, type NativeValue as TestValue, type NativeValues as TestValues, createTestClient };
111
93
  //# sourceMappingURL=configdirector-server-testing.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"configdirector-server-testing.d.cts","names":[],"sources":["../../shared/src/testing/values.ts","../src/testing/TestClient.ts","../../shared/src/errors.ts","../src/testing.ts"],"mappings":";;;KAGY,aAAA,sCAAmD,aAAA;EAAA,CAAqB,GAAA,WAAc,aAAA;AAAA;AAAA,KAEtF,SAAA,+BAAwC,aAAA;EAAA,CAAqB,GAAA,WAAc,aAAA;AAAA;AAAA,KAE3E,UAAA,GAAa,MAAA,SAAe,SAAA;;;;AAJxC;;KCSY,iBAAA;EDTmG;;;;;;ECgB7G,MAAA,GAAS,UAAA;EDdU;;;;ECmBnB,OAAA;EDnBqF;;;ECuBrF,MAAA,GAAS,oBAAA;AAAA;;;;;;;AAhBX;;UA2BiB,UAAA;EAXc;;;;EAAA,SAgBpB,MAAA,EAAQ,oBAAA;EAhBR;;;AAWX;EAWE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,SAAA;;;;;EAM7B,WAAA,CAAY,GAAA;EAMoB;;;;EAAhC,aAAA,CAAc,MAAA,EAAQ,UAAA;EAZO;;;;EAkB7B,kBAAA;EANsB;;;;EAYtB,sBAAA;EAMkB;;;;EAAlB,kBAAA;AAAA;;;cChFW,6BAAA,SAAsC,KAAA;EAAA,SACxB,IAAA;EAAA,SACT,MAAA;cAEJ,OAAA,UAAiB,MAAA;AAAA;AAAA,cAQlB,6BAAA,SAAsC,KAAA;EAAA,SACxB,IAAA;cAEb,OAAA;AAAA;;;;;;;;;;;AFVd;;;;;;;;cGkBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
1
+ {"version":3,"file":"configdirector-server-testing.d.cts","names":[],"sources":["../src/testing/TestClient.ts","../src/testing.ts"],"mappings":";;;;AAYA;;KAAY,iBAAA;EAkBmB;;;;;;EAX7B,MAAA,GAAS,YAAA;EAWoB;AAW/B;;;EAjBE,OAAA;EA4B6B;;;;;EAtB7B,MAAA,GAAS,oBAAA;AAAA;;;;;;;;;UAWM,UAAA;EAmCf;;;;EAAA,SA9BS,MAAA,EAAQ,oBAAA;;;ACvBnB;;ED6BE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,WAAA;EC3B9B;;;;EDiCC,WAAA,CAAY,GAAA;ECjCb;;;;EDuCC,aAAA,CAAc,MAAA,EAAQ,YAAA;;;;;EAMtB,kBAAA;;;;;EAMA,sBAAA;;;;;EAMA,kBAAA;AAAA;;;;;;;;;;;;;AAzCF;;;;;;cClBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
@@ -1,14 +1,5 @@
1
- import { a as ConfigDirectorLogger, t as ConfigDirectorClient } from "./types-BqIJGXWv.mjs";
1
+ import { d as NativeValues, i as ConfigDirectorClient, l as NativeJsonValue, m as ConfigDirectorLogger, r as ConfigDirectorValidationError, t as ConfigDirectorConnectionError, u as NativeValue } from "./errors-BCEfxVqM.mjs";
2
2
 
3
- //#region ../shared/src/testing/values.d.ts
4
- type TestJsonValue = boolean | number | string | null | TestJsonValue[] | {
5
- [key: string]: TestJsonValue;
6
- };
7
- type TestValue = boolean | number | string | TestJsonValue[] | {
8
- [key: string]: TestJsonValue;
9
- };
10
- type TestValues = Record<string, TestValue>;
11
- //#endregion
12
3
  //#region src/testing/TestClient.d.ts
13
4
  /**
14
5
  * Options for {@link createTestClient}. Every option is optional.
@@ -20,14 +11,16 @@ type TestClientOptions = {
20
11
  * number (`float`), a string, or a plain object or array (`json`). Every context receives the
21
12
  * same value.
22
13
  */
23
- values?: TestValues;
14
+ values?: NativeValues;
24
15
  /**
25
16
  * The connection timeout of the SDK client, in milliseconds. It bounds how long a held
26
17
  * `initialize` waits. Defaults to the SDK's production timeout.
27
18
  */
28
19
  timeout?: number;
29
20
  /**
30
- * The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level.
21
+ * The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level. Since
22
+ * the test client sends no telemetry, every read that supplies a context with an invalid kind
23
+ * logs an error, rather than each invalid kind once per telemetry interval.
31
24
  */
32
25
  logger?: ConfigDirectorLogger;
33
26
  };
@@ -49,7 +42,7 @@ interface TestClient {
49
42
  * Stores `value` under `key` and, when the client is connected, delivers it as an update, so
50
43
  * reads, watchers, and `configsUpdated` handlers see it.
51
44
  */
52
- setValue(key: string, value: TestValue): void;
45
+ setValue(key: string, value: NativeValue): void;
53
46
  /**
54
47
  * Removes `key` and, when the client is connected, delivers a full update without it, so reads
55
48
  * fall back to the in-code default value and watchers of `key` receive that default.
@@ -59,7 +52,7 @@ interface TestClient {
59
52
  * Replaces every stored value with `values`, disarms any armed hold or failure, and, when the
60
53
  * client is connected, delivers the new values as a full update.
61
54
  */
62
- replaceValues(values: TestValues): void;
55
+ replaceValues(values: NativeValues): void;
63
56
  /**
64
57
  * Makes the next `initialize` wait until {@link completeInitialization} or
65
58
  * {@link failInitialization} is called, or until the client's timeout elapses.
@@ -77,17 +70,6 @@ interface TestClient {
77
70
  failInitialization(): void;
78
71
  }
79
72
  //#endregion
80
- //#region ../shared/src/errors.d.ts
81
- declare class ConfigDirectorConnectionError extends Error {
82
- readonly name: string;
83
- readonly status?: number;
84
- constructor(message: string, status?: number);
85
- }
86
- declare class ConfigDirectorValidationError extends Error {
87
- readonly name: string;
88
- constructor(message: string);
89
- }
90
- //#endregion
91
73
  //#region src/testing.d.ts
92
74
  /**
93
75
  * Creates a {@link TestClient}: a real `ConfigDirectorClient` connected to an in-memory server
@@ -107,5 +89,5 @@ declare class ConfigDirectorValidationError extends Error {
107
89
  */
108
90
  declare const createTestClient: (options?: TestClientOptions) => TestClient;
109
91
  //#endregion
110
- export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type TestJsonValue, type TestValue, type TestValues, createTestClient };
92
+ export { ConfigDirectorConnectionError, ConfigDirectorValidationError, type TestClient, type TestClientOptions, type NativeJsonValue as TestJsonValue, type NativeValue as TestValue, type NativeValues as TestValues, createTestClient };
111
93
  //# sourceMappingURL=configdirector-server-testing.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"configdirector-server-testing.d.mts","names":[],"sources":["../../shared/src/testing/values.ts","../src/testing/TestClient.ts","../../shared/src/errors.ts","../src/testing.ts"],"mappings":";;;KAGY,aAAA,sCAAmD,aAAA;EAAA,CAAqB,GAAA,WAAc,aAAA;AAAA;AAAA,KAEtF,SAAA,+BAAwC,aAAA;EAAA,CAAqB,GAAA,WAAc,aAAA;AAAA;AAAA,KAE3E,UAAA,GAAa,MAAA,SAAe,SAAA;;;;AAJxC;;KCSY,iBAAA;EDTmG;;;;;;ECgB7G,MAAA,GAAS,UAAA;EDdU;;;;ECmBnB,OAAA;EDnBqF;;;ECuBrF,MAAA,GAAS,oBAAA;AAAA;;;;;;;AAhBX;;UA2BiB,UAAA;EAXc;;;;EAAA,SAgBpB,MAAA,EAAQ,oBAAA;EAhBR;;;AAWX;EAWE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,SAAA;;;;;EAM7B,WAAA,CAAY,GAAA;EAMoB;;;;EAAhC,aAAA,CAAc,MAAA,EAAQ,UAAA;EAZO;;;;EAkB7B,kBAAA;EANsB;;;;EAYtB,sBAAA;EAMkB;;;;EAAlB,kBAAA;AAAA;;;cChFW,6BAAA,SAAsC,KAAA;EAAA,SACxB,IAAA;EAAA,SACT,MAAA;cAEJ,OAAA,UAAiB,MAAA;AAAA;AAAA,cAQlB,6BAAA,SAAsC,KAAA;EAAA,SACxB,IAAA;cAEb,OAAA;AAAA;;;;;;;;;;;AFVd;;;;;;;;cGkBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
1
+ {"version":3,"file":"configdirector-server-testing.d.mts","names":[],"sources":["../src/testing/TestClient.ts","../src/testing.ts"],"mappings":";;;;AAYA;;KAAY,iBAAA;EAkBmB;;;;;;EAX7B,MAAA,GAAS,YAAA;EAWoB;AAW/B;;;EAjBE,OAAA;EA4B6B;;;;;EAtB7B,MAAA,GAAS,oBAAA;AAAA;;;;;;;;;UAWM,UAAA;EAmCf;;;;EAAA,SA9BS,MAAA,EAAQ,oBAAA;;;ACvBnB;;ED6BE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,WAAA;EC3B9B;;;;EDiCC,WAAA,CAAY,GAAA;ECjCb;;;;EDuCC,aAAA,CAAc,MAAA,EAAQ,YAAA;;;;;EAMtB,kBAAA;;;;;EAMA,sBAAA;;;;;EAMA,kBAAA;AAAA;;;;;;;;;;;;;AAzCF;;;;;;cClBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}