@configdirector/nextjs-sdk 1.9.1 → 1.10.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.
Files changed (49) hide show
  1. package/dist/client/index.cjs +1 -1
  2. package/dist/client/index.d.cts +1 -1
  3. package/dist/client/index.d.mts +1 -1
  4. package/dist/client/index.mjs +1 -1
  5. package/dist/client/index.mjs.map +1 -1
  6. package/dist/client/installedTestClient-CW29YuQC.cjs +6 -0
  7. package/dist/client/installedTestClient-CpjJvHZD.mjs +7 -0
  8. package/dist/client/installedTestClient-CpjJvHZD.mjs.map +1 -0
  9. package/dist/client/testing.cjs +1 -1
  10. package/dist/client/testing.d.cts +3 -2
  11. package/dist/client/testing.d.cts.map +1 -1
  12. package/dist/client/testing.d.mts +3 -2
  13. package/dist/client/testing.d.mts.map +1 -1
  14. package/dist/client/testing.mjs +1 -1
  15. package/dist/client/testing.mjs.map +1 -1
  16. package/dist/client/{types-fL7X5zwg.d.cts → types-Cz68hQ6f.d.cts} +35 -14
  17. package/dist/client/types-Cz68hQ6f.d.cts.map +1 -0
  18. package/dist/client/{types-DA2VZKNX.d.mts → types-De4z0tu4.d.mts} +35 -14
  19. package/dist/client/types-De4z0tu4.d.mts.map +1 -0
  20. package/dist/server/{DefaultConfigDirectorClient-ied5Boyk.mjs → DefaultConfigDirectorClient-BTTja_j3.mjs} +2 -2
  21. package/dist/server/DefaultConfigDirectorClient-BTTja_j3.mjs.map +1 -0
  22. package/dist/server/{DefaultConfigDirectorClient-Cf73kJBQ.cjs → DefaultConfigDirectorClient-CqXqwNpW.cjs} +1 -1
  23. package/dist/server/index.cjs +1 -1
  24. package/dist/server/index.d.cts +1 -1
  25. package/dist/server/index.d.cts.map +1 -1
  26. package/dist/server/index.d.mts +1 -1
  27. package/dist/server/index.d.mts.map +1 -1
  28. package/dist/server/index.mjs +1 -1
  29. package/dist/server/index.mjs.map +1 -1
  30. package/dist/server/testing.cjs +1 -1
  31. package/dist/server/testing.d.cts +4 -2
  32. package/dist/server/testing.d.cts.map +1 -1
  33. package/dist/server/testing.d.mts +4 -2
  34. package/dist/server/testing.d.mts.map +1 -1
  35. package/dist/server/testing.mjs +1 -1
  36. package/dist/server/testing.mjs.map +1 -1
  37. package/dist/server/{types-DnzM2ukj.d.cts → types-BBEEMV5Y.d.cts} +23 -10
  38. package/dist/server/types-BBEEMV5Y.d.cts.map +1 -0
  39. package/dist/server/{types-qIeNbtNf.d.mts → types-B_mGbQaa.d.mts} +23 -10
  40. package/dist/server/types-B_mGbQaa.d.mts.map +1 -0
  41. package/package.json +1 -1
  42. package/dist/client/installedTestClient-CDdDsfVQ.mjs +0 -7
  43. package/dist/client/installedTestClient-CDdDsfVQ.mjs.map +0 -1
  44. package/dist/client/installedTestClient-DH7rRR-J.cjs +0 -6
  45. package/dist/client/types-DA2VZKNX.d.mts.map +0 -1
  46. package/dist/client/types-fL7X5zwg.d.cts.map +0 -1
  47. package/dist/server/DefaultConfigDirectorClient-ied5Boyk.mjs.map +0 -1
  48. package/dist/server/types-DnzM2ukj.d.cts.map +0 -1
  49. package/dist/server/types-qIeNbtNf.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installedTestClient-CpjJvHZD.mjs","names":["this","this","this","this","this"],"sources":["../../../js-client-core/src/Emitter.ts","../../../shared/src/errors.ts","../../../eventsource/src/errors.ts","../../../eventsource/src/EventSourceParser.ts","../../../eventsource/src/types.ts","../../../eventsource/src/EventSourceClient.ts","../../../eventsource/src/index.ts","../../../js-client-core/src/StreamingTransport.ts","../../../shared/src/value-parser.ts","../../../shared/src/logger.ts","../../../js-client-core/src/logger.ts","../../../shared/src/url.ts","../../../shared/src/constants.ts","../../../shared/src/transport/pollingInterval.ts","../../../shared/src/instance-id.ts","../../../js-client-core/src/client-session.ts","../../../shared/src/transport/AbstractPollingTransport.ts","../../../shared/src/fetchWithTimeout.ts","../../../js-client-core/src/PollingTransport.ts","../../../js-client-core/src/browser-globals.ts","../../../shared/src/context-kind.ts","../../../js-client-core/src/DefaultConfigDirectorClient.ts","../../../js-client-core/src/installedTestClient.ts"],"sourcesContent":["type EventType = string | symbol;\ntype EventsRecord = Record<EventType, any>;\n\ntype Handler<TEventsRecord extends EventsRecord, TKey extends keyof TEventsRecord> = (\n payload: TEventsRecord[TKey],\n) => void;\n\nexport interface EventProvider<TEventsRecord extends EventsRecord> {\n on<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>): void;\n off<TName extends keyof TEventsRecord>(name: TName, handler?: Handler<TEventsRecord, TName>): void;\n clear(): void;\n}\n\nexport class Emitter<TEventsRecord extends EventsRecord> implements EventProvider<TEventsRecord> {\n private handlerMap: Map<keyof TEventsRecord, Array<(payload: any) => void>> = new Map();\n\n on<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>) {\n const handlers = this.handlerMap.get(name);\n if (handlers) {\n handlers.push(handler);\n } else {\n this.handlerMap.set(name, [handler]);\n }\n }\n\n once<TName extends keyof TEventsRecord>(name: TName, handler: Handler<TEventsRecord, TName>) {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n function onceHandler(payload: TEventsRecord[keyof TEventsRecord]) {\n self.off(name, onceHandler);\n handler.apply(self, payload);\n }\n this.on(name, onceHandler);\n }\n\n off<TName extends keyof TEventsRecord>(name: TName, handler?: Handler<TEventsRecord, TName>) {\n const handlers = this.handlerMap.get(name);\n if (!handlers) {\n return;\n }\n\n if (handler) {\n const listenerIndex = handlers.indexOf(handler);\n if (listenerIndex >= 0) {\n handlers.splice(listenerIndex, 1);\n }\n } else {\n this.handlerMap.set(name, []);\n }\n }\n\n clear() {\n this.handlerMap.clear();\n }\n\n emit<TName extends keyof TEventsRecord>(name: TName, payload: TEventsRecord[TName]) {\n const handlers = this.handlerMap.get(name);\n if (!handlers) {\n return;\n }\n\n handlers.slice().map((h) => h(payload));\n }\n}\n","export class ConfigDirectorConnectionError extends Error {\n public override readonly name: string = \"ConfigDirectorConnectionError\";\n public readonly status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.status = status;\n\n Object.setPrototypeOf(this, ConfigDirectorConnectionError.prototype);\n }\n}\n\nexport class ConfigDirectorValidationError extends Error {\n public override readonly name: string = \"ConfigDirectorValidationError\";\n\n constructor(message: string) {\n super(message);\n\n Object.setPrototypeOf(this, ConfigDirectorValidationError.prototype);\n }\n}\n\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 {\n ConfigDirectorContext,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { Emitter } from \"./Emitter\";\nimport { ConfigDirectorConnectionError } from \"./errors\";\nimport { createEventSourceClient } from \"@eventsource/index\";\nimport type { EventSourceClient } from \"@eventsource/EventSourceClient\";\nimport type { UrlLike } from \"@shared/url\";\n\nexport class StreamingTransport implements Transport {\n private logger: ConfigDirectorLogger;\n private eventSource: EventSourceClient | undefined;\n private eventEmitter = new Emitter<TransportEvents>();\n private url: UrlLike;\n private timeoutTimer: ReturnType<typeof setTimeout> | undefined;\n private connectionGeneration = 0;\n\n constructor(private readonly options: TransportOptions) {\n this.options = options;\n this.logger = options.logger;\n this.url = options.resolveUrl(\"client/sse/v1\", options.baseUrl);\n }\n\n public async connect(context: ConfigDirectorContext, timeout: number): Promise<this> {\n const generation = ++this.connectionGeneration;\n const clientSession = await this.options.clientSession;\n if (generation !== this.connectionGeneration) {\n return this;\n }\n\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 fetch: this.options.fetch,\n body: JSON.stringify({\n givenContext: context,\n metaContext: this.options.metaContext,\n clientSdkKey: this.options.clientSdkKey,\n instanceId: clientSession.instanceId,\n ephemeral: clientSession.ephemeral,\n }),\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 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 delay = this.options.connectionRetryDelay(state.attempt);\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 onError: (error) => {\n this.logger.debug(\"[StreamingTransport] Error: \", error);\n },\n onDisconnect: () => {\n this.logger.debug(\"[StreamingTransport] Disconnected\");\n },\n });\n this.eventSource.connect();\n });\n\n clearTimeout(this.timeoutTimer);\n this.timeoutTimer = undefined;\n return Promise.race([\n eventSourcePromise,\n new Promise<this>((resolve) => {\n this.timeoutTimer = setTimeout(() => resolve(this), timeout);\n }),\n ]).finally(() => clearTimeout(this.timeoutTimer));\n }\n\n private dispatchMessage(data: string) {\n try {\n const json = JSON.parse(data);\n this.eventEmitter.emit(\"configSetReceived\", json);\n } catch (error) {\n this.logger.error(\"[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 on<TName extends keyof TransportEvents>(\n name: TName,\n handler: (payload: TransportEvents[TName]) => void,\n ): void {\n this.eventEmitter.on(name, handler);\n }\n\n public off<TName extends keyof TransportEvents>(\n name: TName,\n handler?: ((payload: TransportEvents[TName]) => void) | undefined,\n ): void {\n this.eventEmitter.off(name, handler);\n }\n\n public clear(): void {\n this.eventEmitter.clear();\n }\n\n public close() {\n this.connectionGeneration++;\n this.eventSource?.close();\n clearTimeout(this.timeoutTimer);\n }\n\n public dispose(): void {\n this.close();\n this.clear();\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 { type ConfigDirectorLogMessageDecorator, type ConfigDirectorLoggingLevel } from \"./types\";\nimport { DefaultConsoleLogger } from \"../../shared/src/logger\";\nexport { DefaultConsoleLogger } from \"../../shared/src/logger\";\n\nclass LogMessageDecorator implements ConfigDirectorLogMessageDecorator {\n decorateMessage(message: string): string {\n return `[ConfigDirector:js-client-sdk] ${message}`;\n }\n}\n\nexport const createDefaultLogger = (\n level?: ConfigDirectorLoggingLevel,\n messageDecorator?: ConfigDirectorLogMessageDecorator,\n) => {\n return new DefaultConsoleLogger(level ?? \"warn\", messageDecorator ?? new LogMessageDecorator());\n};\n","import { 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 const CLIENT_BASE_URL = \"https://client-sdk-api.configdirector.com\";\nexport const CLIENT_DEFAULT_POLLING_INTERVAL_SECONDS = 60;\nexport const CLIENT_MIN_POLLING_INTERVAL_SECONDS = 30;\nexport const SERVER_DEFAULT_POLLING_INTERVAL_SECONDS = 300;\nexport const SERVER_MIN_POLLING_INTERVAL_SECONDS = 60;\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","// React Native's Hermes engine does not expose a global `crypto`, unlike browsers and Node 19+.\nexport const generateInstanceId = (): string => {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (placeholder) => {\n const random = (Math.random() * 16) | 0;\n const value = placeholder === \"x\" ? random : (random & 0x3) | 0x8;\n return value.toString(16);\n });\n};\n","import { generateInstanceId } from \"@shared/instance-id\";\nimport type { ClientSession, ConfigDirectorLogger, InstanceIdStorage } from \"./types\";\n\nconst STORAGE_KEY = \"configdirector-sdk:instance-id\";\nconst RECORD_VERSION = 1;\nconst MAX_AGE_MS = 6 * 60 * 60 * 1_000;\nconst STORAGE_DEADLINE_MS = 1_000;\nconst CHECKSUM_SALT = \"configdirector.instanceId.v1\";\nconst UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;\n\ntype StoredInstanceId = {\n v: number;\n id: string;\n createdAt: number;\n checksum: string;\n};\n\ntype StorageRead =\n | { outcome: \"stored\"; instanceId: string }\n | { outcome: \"nothing usable\" }\n | { outcome: \"failed\" };\n\ntype Deadline = { passed: boolean };\n\nconst fnv1a = (input: string, offsetBasis: number): number => {\n let hash = offsetBasis;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\n};\n\nconst checksumFor = (id: string, createdAt: number): string => {\n const payload = `${CHECKSUM_SALT}|${id}|${createdAt}`;\n const low = fnv1a(payload, 0x811c9dc5);\n const high = fnv1a(`${low}|${payload}`, 0x01000193);\n return high.toString(16).padStart(8, \"0\") + low.toString(16).padStart(8, \"0\");\n};\n\nconst localStorageIfAvailable = (): InstanceIdStorage | undefined => {\n try {\n return (globalThis as { localStorage?: Storage }).localStorage ?? undefined;\n } catch {\n return undefined;\n }\n};\n\nconst isUsableRecord = (record: unknown): record is StoredInstanceId => {\n if (!record || typeof record !== \"object\") {\n return false;\n }\n\n const { v, id, createdAt, checksum } = record as Partial<StoredInstanceId>;\n if (v !== RECORD_VERSION || typeof id !== \"string\" || typeof checksum !== \"string\") {\n return false;\n }\n\n if (typeof createdAt !== \"number\" || !Number.isInteger(createdAt) || createdAt <= 0) {\n return false;\n }\n\n if (!UUID_V4_PATTERN.test(id)) {\n return false;\n }\n\n return checksum === checksumFor(id, createdAt);\n};\n\nconst readStoredInstanceId = async (\n storage: InstanceIdStorage,\n logger: ConfigDirectorLogger,\n): Promise<StorageRead> => {\n let raw: unknown;\n try {\n raw = await storage.getItem(STORAGE_KEY);\n } catch (error) {\n logger.debug(\n \"[ConfigDirectorClient] Could not read the stored instance id, using an ephemeral one\",\n error,\n );\n return { outcome: \"failed\" };\n }\n\n if (typeof raw !== \"string\" || raw === \"\") {\n return { outcome: \"nothing usable\" };\n }\n\n let record: unknown;\n try {\n record = JSON.parse(raw);\n } catch {\n logger.debug(\"[ConfigDirectorClient] The stored instance id is not valid JSON, generating a new one\");\n return { outcome: \"nothing usable\" };\n }\n\n if (!isUsableRecord(record)) {\n logger.debug(\"[ConfigDirectorClient] The stored instance id failed validation, generating a new one\");\n return { outcome: \"nothing usable\" };\n }\n\n const age = Date.now() - record.createdAt;\n if (age < 0 || age >= MAX_AGE_MS) {\n logger.debug(\"[ConfigDirectorClient] The stored instance id has expired, generating a new one\");\n return { outcome: \"nothing usable\" };\n }\n\n return { outcome: \"stored\", instanceId: record.id };\n};\n\nconst persistInstanceId = async (\n storage: InstanceIdStorage,\n id: string,\n logger: ConfigDirectorLogger,\n): Promise<boolean> => {\n const createdAt = Date.now();\n const record: StoredInstanceId = { v: RECORD_VERSION, id, createdAt, checksum: checksumFor(id, createdAt) };\n try {\n await storage.setItem(STORAGE_KEY, JSON.stringify(record));\n return true;\n } catch (error) {\n logger.debug(\n \"[ConfigDirectorClient] Could not persist the instance id, using it as an ephemeral one\",\n error,\n );\n return false;\n }\n};\n\nconst settleFromStorage = async (\n storage: InstanceIdStorage,\n provisionalId: string,\n deadline: Deadline,\n logger: ConfigDirectorLogger,\n): Promise<ClientSession> => {\n const read = await readStoredInstanceId(storage, logger);\n switch (read.outcome) {\n case \"stored\":\n return { instanceId: read.instanceId };\n case \"failed\":\n return { instanceId: provisionalId, ephemeral: true };\n case \"nothing usable\":\n break;\n }\n\n if (deadline.passed) {\n return { instanceId: provisionalId, ephemeral: true };\n }\n\n const persisted = await persistInstanceId(storage, provisionalId, logger);\n return persisted ? { instanceId: provisionalId } : { instanceId: provisionalId, ephemeral: true };\n};\n\nexport const resolveClientSession = (\n logger: ConfigDirectorLogger,\n storage: InstanceIdStorage | undefined = localStorageIfAvailable(),\n): Promise<ClientSession> => {\n const provisionalId = generateInstanceId();\n if (!storage) {\n logger.debug(\n \"[ConfigDirectorClient] No instance id storage is available, using an ephemeral instance id\",\n );\n return Promise.resolve({ instanceId: provisionalId, ephemeral: true });\n }\n\n const deadline: Deadline = { passed: false };\n let deadlineTimer: ReturnType<typeof setTimeout> | undefined;\n const fallbackAtDeadline = new Promise<ClientSession>((resolve) => {\n deadlineTimer = setTimeout(() => {\n deadline.passed = true;\n logger.warn(\n `[ConfigDirectorClient] The instance id storage did not respond within ${STORAGE_DEADLINE_MS}ms, using an ephemeral instance id`,\n );\n resolve({ instanceId: provisionalId, ephemeral: true });\n }, STORAGE_DEADLINE_MS);\n });\n\n return Promise.race([\n settleFromStorage(storage, provisionalId, deadline, logger),\n fallbackAtDeadline,\n ]).finally(() => clearTimeout(deadlineTimer));\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 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 ClientSession,\n ConfigDirectorContext,\n ConfigDirectorLogger,\n Transport,\n TransportEvents,\n TransportOptions,\n} from \"./types\";\nimport { Emitter } from \"./Emitter\";\nimport { AbstractPollingTransport } from \"@shared/transport/AbstractPollingTransport\";\nimport { fetchWithTimeout } from \"@shared/fetchWithTimeout\";\nimport type { UrlLike } from \"@shared/url\";\nimport { CLIENT_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 Emitter<TransportEvents>();\n private url: UrlLike;\n private lastUpdateTimestamp: string | undefined;\n private connectionGeneration = 0;\n\n constructor(private readonly options: TransportOptions) {\n super();\n this.options = options;\n this.logger = options.logger;\n this.url = options.resolveUrl(\"client/polling/v1\", options.baseUrl);\n this.pollingIntervalSeconds = options.pollingInterval ?? CLIENT_DEFAULT_POLLING_INTERVAL_SECONDS;\n }\n\n public async connect(context: ConfigDirectorContext, timeout: number): Promise<this> {\n const generation = ++this.connectionGeneration;\n this.clearPollingInterval();\n const clientSession = await this.options.clientSession;\n if (generation !== this.connectionGeneration) {\n return this;\n }\n\n try {\n await this.fetchConfigs(context, timeout, clientSession);\n } finally {\n // A transient failure on the first fetch must not leave the client 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(context, timeout, clientSession).catch((error) => {\n this.logger.error(\"[PollingTransport] Error during polling:\", error);\n });\n });\n }\n }\n\n return this;\n }\n\n public override close() {\n this.connectionGeneration++;\n super.close();\n }\n\n private async fetchConfigs(context: ConfigDirectorContext, timeout: number, clientSession: ClientSession) {\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 givenContext: context,\n metaContext: this.options.metaContext,\n clientSdkKey: this.options.clientSdkKey,\n instanceId: clientSession.instanceId,\n ephemeral: clientSession.ephemeral,\n lastUpdateTimestamp: this.lastUpdateTimestamp,\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(\"configSetReceived\", json);\n }\n } catch (fetchError) {\n try {\n this.handleFetchError(fetchError);\n } catch (handledError) {\n if (this.fatalError) {\n this.eventEmitter.emit(\"connectionError\", handledError as Error);\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 this.eventEmitter.off(name, handler);\n }\n\n public clear(): void {\n this.eventEmitter.clear();\n }\n}\n","type BrowserGlobals = {\n navigator?: { userAgent?: string };\n location?: { host?: string };\n};\n\nconst read = (readValue: (globals: BrowserGlobals) => string | undefined): string | undefined => {\n try {\n return readValue(globalThis as BrowserGlobals);\n } catch {\n return undefined;\n }\n};\n\nexport const readUserAgent = (): string | undefined => read((globals) => globals.navigator?.userAgent);\n\nexport const readHost = (): string | undefined => read((globals) => globals.location?.host);\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 { Emitter } from \"./Emitter\";\nimport { StreamingTransport } from \"./StreamingTransport\";\nimport { getRequestedType, parseConfigValue } from \"../../shared/src/value-parser\";\nimport type {\n ConfigSet,\n ConfigState,\n ConfigDirectorContext,\n ConfigDirectorClientOptions,\n ConfigDirectorClient,\n ClientEvents,\n WatchHandler,\n ConfigValueType,\n ConfigDirectorLogger,\n Transport,\n ClientConnectAction,\n IdentifyingSdkOptions,\n InternalClientOptions,\n ConnectionMode,\n ConfigEvaluation,\n HookHandler,\n ClientSession,\n} from \"./types\";\nimport { createDefaultLogger } from \"./logger\";\nimport { ConfigDirectorValidationError } from \"./errors\";\nimport type { TelemetryClient } from \"./telemetry\";\nimport { defaultUrlFactory } from \"@shared/url\";\nimport type { UrlFactory, UrlLike } from \"@shared/url\";\nimport {\n CLIENT_BASE_URL,\n CLIENT_DEFAULT_POLLING_INTERVAL_SECONDS,\n CLIENT_MIN_POLLING_INTERVAL_SECONDS,\n} from \"@shared/constants\";\nimport { resolvePollingInterval } from \"@shared/transport/pollingInterval\";\nimport { resolveClientSession } from \"./client-session\";\nimport { PollingTransport } from \"./PollingTransport\";\nimport { readHost, readUserAgent } from \"./browser-globals\";\nimport { admitContext } from \"@shared/context-kind\";\nconst MAX_EXPONENTIAL_DELAY = 9; // 2^9 = 512 seconds, to cap it to under 10min\n\ntype ContextSubmission = Exclude<ClientConnectAction, \"network resume\">;\n\ntype WatchHandlerWithOptions<T extends ConfigValueType> = {\n handler: WatchHandler<T>;\n defaultValue: T;\n requestedType: string;\n};\n\nexport class DefaultConfigDirectorClient implements ConfigDirectorClient {\n private logger: ConfigDirectorLogger;\n private telemetryClient: TelemetryClient;\n private configSet: ConfigSet | undefined;\n private handlersMap: Map<string, WatchHandlerWithOptions<any>[]> = new Map();\n private transport: Transport;\n private eventEmitter = new Emitter<ClientEvents>();\n private timeout: number;\n private timeoutTimer: ReturnType<typeof setTimeout> | undefined;\n private ready = false;\n private initializing = false;\n private hasReceivedConfigSet = false;\n private readyPromise: Promise<void> | undefined;\n private readyResolve: ((cancelled?: boolean) => void) | undefined;\n private currentContext?: ConfigDirectorContext;\n private connectionMode: ConnectionMode;\n private clientSession: Promise<ClientSession>;\n\n constructor(\n telemetryClient: TelemetryClient,\n clientSdkKey: string,\n sdkOptions: IdentifyingSdkOptions,\n clientOptions?: ConfigDirectorClientOptions,\n internalClientOptions?: InternalClientOptions,\n ) {\n this.logger = clientOptions?.logger ?? createDefaultLogger();\n this.clientSession = resolveClientSession(this.logger, internalClientOptions?.instanceIdStorage);\n this.timeout = clientOptions?.connection?.timeout ?? 3_000;\n this.validateSdkKeyPresence(clientSdkKey);\n const urlFactory: UrlFactory = internalClientOptions?.urlFactory ?? defaultUrlFactory;\n const baseUrl = this.parseUrl(clientOptions?.connection?.url, urlFactory) ?? CLIENT_BASE_URL;\n this.connectionMode = clientOptions?.connection?.mode ?? \"streaming\";\n this.telemetryClient = telemetryClient;\n this.transport =\n internalClientOptions?.transport ??\n this.buildTransport(clientSdkKey, sdkOptions, baseUrl, urlFactory, clientOptions, internalClientOptions);\n\n this.transport.on(\"configSetReceived\", (configSet: ConfigSet) => {\n this.hasReceivedConfigSet = true;\n this.readyResolve?.();\n const removedKeys = this.findKeysRemovedBy(configSet);\n if (!this.configSet || configSet.kind == \"full\") {\n this.configSet = configSet;\n } else {\n this.configSet.configs = {\n ...this.configSet.configs,\n ...configSet.configs,\n };\n }\n const keys = Object.keys(configSet.configs);\n this.emit(\"configsUpdated\", { keys, removedKeys });\n this.updateWatchers([...keys, ...removedKeys]);\n this.logger.debug(\"[ConfigDirectorClient] ConfigSet updated from server:\", { keys, removedKeys });\n });\n\n this.transport.on(\"connectionError\", (error: Error) => {\n this.initializing = false;\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.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(\"contextUpdated\", clientOptions?.hooks?.contextUpdated);\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 private resolvePollingIntervalForMode(configuredSeconds: number | undefined): number | undefined {\n if (this.connectionMode !== \"polling\") {\n return undefined;\n }\n const resolved = resolvePollingInterval(configuredSeconds, {\n defaultSeconds: CLIENT_DEFAULT_POLLING_INTERVAL_SECONDS,\n minimumSeconds: CLIENT_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 buildTransport(\n clientSdkKey: string,\n sdkOptions: IdentifyingSdkOptions,\n baseUrl: UrlLike,\n urlFactory: UrlFactory,\n clientOptions: ConfigDirectorClientOptions | undefined,\n internalClientOptions: InternalClientOptions | undefined,\n ): Transport {\n const transportConstructor = this.getTransportConstructor(this.connectionMode);\n return new transportConstructor({\n clientSession: this.clientSession,\n clientSdkKey,\n baseUrl,\n resolveUrl: urlFactory,\n metaContext: {\n ...clientOptions?.metadata,\n sdkName: sdkOptions.sdkName,\n sdkVersion: sdkOptions.sdkVersion,\n userAgent: readUserAgent(),\n host: readHost(),\n },\n logger: this.logger,\n fetch: internalClientOptions?.fetch,\n connectionRetryDelay:\n internalClientOptions?.connectionRetryDelay ??\n ((attempt: number) => {\n const seconds = Math.pow(2, Math.min(attempt, MAX_EXPONENTIAL_DELAY));\n return seconds * 1_000;\n }),\n pollingInterval: this.resolvePollingIntervalForMode(clientOptions?.connection?.pollingInterval),\n });\n }\n\n private getTransportConstructor(connectionMode?: ConnectionMode) {\n switch (connectionMode) {\n case \"polling\":\n return PollingTransport;\n default:\n return StreamingTransport;\n }\n }\n\n public async initialize(context?: ConfigDirectorContext) {\n if (!this.hasReceivedConfigSet) {\n this.initializing = true;\n }\n await this.submitContext(context, \"initialization\");\n }\n\n public async updateContext(context: ConfigDirectorContext) {\n await this.submitContext(context, \"context update\");\n }\n\n private async submitContext(context: ConfigDirectorContext | undefined, submission: ContextSubmission) {\n await this.connectToTransport(this.contextToConnectWith(context, submission), submission);\n }\n\n private contextToConnectWith(\n suppliedContext: ConfigDirectorContext | undefined,\n submission: ContextSubmission,\n ): ConfigDirectorContext | undefined {\n const admission = admitContext(suppliedContext);\n switch (admission.outcome) {\n case \"admitted\":\n return admission.context;\n case \"ignored\":\n this.logger.error(\n `[ConfigDirectorClient] Ignored the context supplied for ${submission} because its kind '${admission.kindSample}' is invalid; connecting as if no context was supplied. A context kind has 1 to 64 characters, using only lowercase letters, digits, '-' and '_'.`,\n );\n this.telemetryClient.recordInvalidContext(admission.kindSample);\n return undefined;\n }\n }\n\n public get context(): ConfigDirectorContext | undefined {\n return this.currentContext;\n }\n\n private async connectToTransport(context: ConfigDirectorContext | undefined, caller: ClientConnectAction) {\n try {\n this.ready = false;\n this.readyPromise = new Promise<boolean | undefined>((resolve) => {\n this.readyResolve = resolve;\n }).then((cancelled) => {\n if (cancelled) {\n return;\n }\n this.ready = true;\n this.initializing = false;\n this.emit(\"clientReady\", { action: caller });\n this.logger.debug(\"[ConfigDirectorClient] Received initial payload from the server, client is ready\");\n });\n const startTime = new Date().getTime();\n await this.transport.connect(context ?? {}, this.timeout, caller);\n this.currentContext = context;\n this.telemetryClient\n .updateContext(context)\n .catch((error) =>\n this.logger.warn(\"[ConfigDirectorClient] Telemetry could not update the context: \", error),\n );\n this.emit(\"contextUpdated\", { context });\n\n const elapsedTime = new Date().getTime() - startTime;\n const remainingTimeout = this.timeout - elapsedTime;\n clearTimeout(this.timeoutTimer);\n this.timeoutTimer = undefined;\n if (remainingTimeout > 0) {\n await Promise.race([\n this.readyPromise,\n new Promise<void>((resolve) => {\n this.timeoutTimer = setTimeout(() => resolve(), remainingTimeout);\n }),\n ]).finally(() => clearTimeout(this.timeoutTimer));\n }\n\n if (!this.ready) {\n const warningDetails =\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 this.logger.warn(\n `[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms. ${warningDetails}`,\n );\n }\n } catch (error) {\n this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);\n }\n }\n\n private findKeysRemovedBy(configSet: ConfigSet): string[] {\n if (!this.configSet || configSet.kind != \"full\") {\n return [];\n }\n return Object.keys(this.configSet.configs).filter((key) => !Object.prototype.hasOwnProperty.call(configSet.configs, key));\n }\n\n private updateWatchers(configKeys: string[]) {\n configKeys.forEach((configKey) => this.updateWatchersForConfig(configKey));\n }\n\n private updateWatchersForConfig(configKey: string) {\n const configState = this.configSet?.configs[configKey];\n this.handlersMap.get(configKey)?.forEach((h) => {\n const value = this.getValueFromConfigState(configKey, configState, h.defaultValue);\n h.handler(value);\n });\n }\n\n public watch<T extends ConfigValueType>(configKey: string, defaultValue: T, callback: WatchHandler<T>) {\n this.validateDefaultValue(defaultValue);\n\n const handlers = this.handlersMap.get(configKey);\n const handlerWithOptions = { handler: callback, defaultValue, requestedType: typeof defaultValue };\n if (handlers) {\n handlers.push(handlerWithOptions);\n } else {\n this.handlersMap.set(configKey, [handlerWithOptions]);\n }\n\n return () => this.unwatch(configKey, callback);\n }\n\n public unwatch<T extends ConfigValueType>(configKey: string, callback?: WatchHandler<T>) {\n const handlers = this.handlersMap.get(configKey);\n if (!handlers) {\n return;\n }\n\n if (callback) {\n const index = handlers.findIndex((h) => h.handler == callback);\n if (index >= 0) {\n handlers?.splice(index, 1);\n }\n } else {\n handlers.splice(0);\n }\n }\n\n public getValue<T extends ConfigValueType>(configKey: string, defaultValue: T): T {\n this.validateDefaultValue(defaultValue);\n\n const configState = this.configSet?.configs[configKey];\n return this.getValueFromConfigState(configKey, configState, defaultValue);\n }\n\n private getValueFromConfigState<T extends ConfigValueType>(\n configKey: string,\n configState: ConfigState | undefined,\n defaultValue: T,\n ): T {\n if (!configState) {\n this.logger.debug(\n `[ConfigDirectorClient] No config state found for '${configKey}', returning default value '${defaultValue}'`,\n );\n const reason = this.isReady ? \"config-state-missing\" : \"client-not-ready\";\n this.telemetryClient.evaluatedConfig({\n contextId: this.currentContext?.id,\n key: configKey,\n defaultValue: defaultValue,\n requestedType: getRequestedType(defaultValue),\n evaluatedValue: defaultValue,\n usedDefault: true,\n evaluationReason: reason,\n });\n this.dispatchEvaluationEvent({\n key: configKey,\n value: defaultValue,\n isDefaultValue: true,\n reason,\n context: this.context,\n });\n return defaultValue;\n }\n\n const parseResult = parseConfigValue<T>(configState, defaultValue);\n this.telemetryClient.evaluatedConfig({\n contextId: this.currentContext?.id,\n type: configState.type,\n key: configKey,\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 this.dispatchEvaluationEvent({\n key: configKey,\n value: parseResult.parsedValue,\n valueId: parseResult.parsedValueId ?? undefined,\n isDefaultValue: parseResult.usedDefault,\n reason: parseResult.reason,\n context: this.context,\n });\n this.logger.debug(`[ConfigDirectorClient] Evaluated '${configKey}' to '${parseResult.parsedValue}'`);\n return parseResult.parsedValue;\n }\n\n private dispatchEvaluationEvent(event: ConfigEvaluation) {\n setTimeout(() => this.emit(\"configEvaluated\", { evaluation: event }), 0);\n }\n\n private emit<TName extends keyof ClientEvents>(name: TName, payload: ClientEvents[TName]) {\n try {\n this.eventEmitter.emit(name, payload);\n } catch (error) {\n this.logger.error(`[ConfigDirectorClient] Error executing event handlers for '${name}'`, error);\n }\n }\n\n private parseUrl(url: string | undefined, urlFactory: UrlFactory): UrlLike | undefined {\n if (!url) {\n return;\n }\n\n try {\n return urlFactory(url);\n } catch (error) {\n throw new ConfigDirectorValidationError(`Invalid base URL '${url}'. Parsing failed: ${error}`);\n }\n }\n\n private validateSdkKeyPresence(sdkKey: string | null | undefined) {\n if (!sdkKey || sdkKey.trim().length == 0) {\n throw new ConfigDirectorValidationError(\n \"No client SDK key was provided, the client cannot be instantiated without a valid client SDK key\",\n );\n }\n }\n\n private validateDefaultValue<T extends ConfigValueType>(defaultValue: T) {\n if (defaultValue === undefined || defaultValue === null) {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config must be defined and non-null.\",\n );\n }\n\n if (typeof defaultValue === \"function\") {\n throw new ConfigDirectorValidationError(\n \"Invalid default value. The default value for a config cannot be a function.\",\n );\n }\n }\n\n public get isReady(): boolean {\n return this.ready;\n }\n\n public get isInitializing(): boolean {\n return this.initializing;\n }\n\n public on<T extends keyof ClientEvents>(eventName: T, handler: (event: ClientEvents[T]) => void): void {\n this.eventEmitter.on(eventName, handler);\n }\n\n public off<T extends keyof ClientEvents>(eventName: T, handler?: (payload: ClientEvents[T]) => void) {\n this.eventEmitter.off(eventName, handler);\n }\n\n public clear() {\n this.logger.debug(\"[ConfigDirectorClient] clear() has been called, removing all observers\");\n this.eventEmitter.clear();\n this.handlersMap.clear();\n }\n\n public unwatchAll() {\n this.handlersMap.clear();\n }\n\n public pauseNetwork(): void {\n this.logger.debug(\"[ConfigDirectorClient] pauseNetwork() called, pausing transport connection\");\n clearTimeout(this.timeoutTimer);\n this.timeoutTimer = undefined;\n this.transport.close();\n this.ready = false;\n }\n\n public async resumeNetwork(): Promise<void> {\n await this.connectToTransport(this.currentContext, \"network resume\");\n }\n\n public async close() {\n this.logger.debug(\"[ConfigDirectorClient] close() has been called, closing connection to server\");\n clearTimeout(this.timeoutTimer);\n this.readyResolve?.(true);\n this.telemetryClient\n .close()\n .catch((error) => this.logger.warn(\"[ConfigDirectorClient] Telemetry could not close: \", error));\n this.transport.close();\n this.ready = false;\n this.initializing = false;\n }\n\n public dispose() {\n this.clear();\n this.close();\n }\n}\n","import type { ConfigDirectorClient } from \"./types\";\n\nexport const INSTALLED_TEST_CLIENT_SLOT = Symbol.for(\"@configdirector/installed-test-client\");\n\nexport type InstalledTestClientSlot = { [INSTALLED_TEST_CLIENT_SLOT]?: ConfigDirectorClient };\n\nexport const readInstalledTestClient = (): ConfigDirectorClient | undefined =>\n (globalThis as InstalledTestClientSlot)[INSTALLED_TEST_CLIENT_SLOT];\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- "use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./installedTestClient-DH7rRR-J.cjs`),t=(t,r)=>{if(t.trim().length==0)throw new e.u(`Invalid config key. The key of a test value cannot be empty.`);return n(t,r)},n=(t,n)=>{switch(typeof n){case`boolean`:return{type:`boolean`,value:n?`true`:`false`};case`number`:return r(t,n);case`string`:return{type:`string`,value:n};case`object`:if(n!==null)return a(t,n,``),{type:`json`,value:JSON.stringify(n)}}throw new e.u(`Invalid test value for '${t}': ${c(n)}. Use a boolean, a number, a string, a plain object, or an array.`)},r=(t,n)=>{if(!Number.isFinite(n))throw new e.u(`Invalid test value for '${t}': ${c(n)} is not a finite number.`);if(Number.isInteger(n)){if(!Number.isSafeInteger(n))throw new e.u(`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:i(n)}},i=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}`},a=(e,t,n)=>{if(!(t===null||typeof t==`boolean`||typeof t==`string`)){if(typeof t==`number`){if(!Number.isFinite(t))throw o(e,n,`${c(t)} is not a finite number`);return}if(Array.isArray(t)){t.forEach((t,r)=>a(e,t,`${n}[${r}]`));return}if(s(t)){for(let[r,i]of Object.entries(t))a(e,i,n?`${n}.${r}`:r);return}throw o(e,n,`${c(t)} cannot be encoded as JSON`)}},o=(t,n,r)=>new e.u(`Invalid test value for '${t}'${n?` at '${n}'`:``}: ${r}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`),s=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},c=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),l=(e,n,r)=>{let i=t(e,n);return{id:`test-config:${e}`,key:e,type:i.type,value:i.value,valueId:`test-value:${e}:${r}`}},u={environmentId:`test-environment`,projectId:`test-project`},d=e=>{switch(e){case`initialization`:return`initialize`;case`context update`:return`updateContext`;default:return}};var f=class{constructor(t,n){this.logger=t,this.emitter=new e.d,this.configs=new Map,this.recordedContexts=[],this.pendingDeliveries=[],this.armed={initialize:`nothing`,updateContext:`nothing`},this.connected=!1,this.delivering=!1,this.revision=0,this.attemptGeneration=0,this.on=(e,t)=>this.emitter.on(e,t),this.off=(e,t)=>this.emitter.off(e,t),this.store(n)}get contextUpdates(){return[...this.recordedContexts]}setValue(e,t){let n=l(e,t,++this.revision);this.configs.set(e,n),this.deliverIfConnected({...u,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.initialize=`nothing`,this.armed.updateContext=`nothing`,this.deliverIfConnected(this.fullUpdate())}hold(e){this.armed[e]=`hold`}complete(e){this.pendingHold?.attempt===e?this.pendingHold.complete():this.armed[e]===`hold`&&(this.armed[e]=`nothing`)}fail(e){this.pendingHold?.attempt===e?this.pendingHold.fail():this.armed[e]=`failure`}connect(e,t,n){this.endAttempt();let r=d(n);if(!r)return this.connectNow();this.recordedContexts.push(e);let i=this.armed[r];switch(this.armed[r]=`nothing`,i){case`failure`:return this.failNow(n);case`hold`:return this.holdNow(r,n,t);default:return this.connectNow()}}clear(){this.emitter.clear()}close(){this.endAttempt()}dispose(){this.close(),this.clear()}endAttempt(){this.attemptGeneration++,this.connected=!1,this.pendingHold?.end()}connectNow(){let e=Promise.resolve(this);return this.scheduleFirstDelivery(),e}holdNow(e,t,n){return new Promise((r,i)=>{let a=setTimeout(()=>this.pendingHold?.end(),n),o=()=>{clearTimeout(a),this.pendingHold=void 0};this.pendingHold={attempt:e,end:()=>{o(),r(this)},complete:()=>{o(),r(this),this.scheduleFirstDelivery()},fail:()=>{o();let e=this.fatalError(t);this.emitter.emit(`connectionError`,e),i(e)}}})}failNow(e){let t=this.fatalError(e);return this.emitter.emit(`connectionError`,t),Promise.reject(t)}fatalError(t){return new e.l(`Connection failed with status: 401. Error: the test client failed this ${t}. This is an unrecoverable error, will not attempt to reconnect.`,401)}scheduleFirstDelivery(){let e=this.attemptGeneration;Promise.resolve().then(()=>{}).then(()=>{e===this.attemptGeneration&&(this.connected=!0,this.deliver(this.fullUpdate()))})}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(`configSetReceived`,e)}catch(e){this.logger.error(`[InMemoryConnection] Error dispatching a config update from the test client: `,e)}}fullUpdate(){return{...u,kind:`full`,configs:Object.fromEntries(this.configs)}}store(e){this.encodeAll(e).forEach((e,t)=>this.configs.set(t,e))}encodeAll(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,l(n,r,++this.revision));return t}};const p={updateContext:async()=>{},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(`initialize`)}completeInitialization(){this.connection.complete(`initialize`)}failInitialization(){this.connection.fail(`initialize`)}holdContextUpdate(){this.connection.hold(`updateContext`)}completeContextUpdate(){this.connection.complete(`updateContext`)}failContextUpdate(){this.connection.fail(`updateContext`)}get contextUpdates(){return this.connection.contextUpdates}};const h=(t,n)=>{let r=n?.logger??e.s(),i=new f(r,n?.values??{});return new m(new e.r(p,`test-client`,t,{logger:r,connection:{timeout:n?.timeout}},{transport:i}),i)},g=t=>{let n=globalThis,r=n[e.t];return n[e.t]=t.client,()=>{n[e.t]=r}},_=e=>h({sdkName:`nextjs-sdk`,sdkVersion:`1.9.1`},e);exports.ConfigDirectorConnectionError=e.l,exports.ConfigDirectorValidationError=e.u,exports.createTestClient=_,exports.installTestClient=g;
2
+ "use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./installedTestClient-CW29YuQC.cjs`),t=(t,r)=>{if(t.trim().length==0)throw new e.u(`Invalid config key. The key of a test value cannot be empty.`);return n(t,r)},n=(t,n)=>{switch(typeof n){case`boolean`:return{type:`boolean`,value:n?`true`:`false`};case`number`:return r(t,n);case`string`:return{type:`string`,value:n};case`object`:if(n!==null)return a(t,n,``),{type:`json`,value:JSON.stringify(n)}}throw new e.u(`Invalid test value for '${t}': ${c(n)}. Use a boolean, a number, a string, a plain object, or an array.`)},r=(t,n)=>{if(!Number.isFinite(n))throw new e.u(`Invalid test value for '${t}': ${c(n)} is not a finite number.`);if(Number.isInteger(n)){if(!Number.isSafeInteger(n))throw new e.u(`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:i(n)}},i=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}`},a=(e,t,n)=>{if(!(t===null||typeof t==`boolean`||typeof t==`string`)){if(typeof t==`number`){if(!Number.isFinite(t))throw o(e,n,`${c(t)} is not a finite number`);return}if(Array.isArray(t)){t.forEach((t,r)=>a(e,t,`${n}[${r}]`));return}if(s(t)){for(let[r,i]of Object.entries(t))a(e,i,n?`${n}.${r}`:r);return}throw o(e,n,`${c(t)} cannot be encoded as JSON`)}},o=(t,n,r)=>new e.u(`Invalid test value for '${t}'${n?` at '${n}'`:``}: ${r}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`),s=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},c=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),l=(e,n,r)=>{let i=t(e,n);return{id:`test-config:${e}`,key:e,type:i.type,value:i.value,valueId:`test-value:${e}:${r}`}},u={environmentId:`test-environment`,projectId:`test-project`},d=e=>{switch(e){case`initialization`:return`initialize`;case`context update`:return`updateContext`;default:return}};var f=class{constructor(t,n){this.logger=t,this.emitter=new e.d,this.configs=new Map,this.recordedContexts=[],this.pendingDeliveries=[],this.armed={initialize:`nothing`,updateContext:`nothing`},this.connected=!1,this.delivering=!1,this.revision=0,this.attemptGeneration=0,this.on=(e,t)=>this.emitter.on(e,t),this.off=(e,t)=>this.emitter.off(e,t),this.store(n)}get contextUpdates(){return[...this.recordedContexts]}setValue(e,t){let n=l(e,t,++this.revision);this.configs.set(e,n),this.deliverIfConnected({...u,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.initialize=`nothing`,this.armed.updateContext=`nothing`,this.deliverIfConnected(this.fullUpdate())}hold(e){this.armed[e]=`hold`}complete(e){this.pendingHold?.attempt===e?this.pendingHold.complete():this.armed[e]===`hold`&&(this.armed[e]=`nothing`)}fail(e){this.pendingHold?.attempt===e?this.pendingHold.fail():this.armed[e]=`failure`}connect(e,t,n){this.endAttempt();let r=d(n);if(!r)return this.connectNow();this.recordedContexts.push(e);let i=this.armed[r];switch(this.armed[r]=`nothing`,i){case`failure`:return this.failNow(n);case`hold`:return this.holdNow(r,n,t);default:return this.connectNow()}}clear(){this.emitter.clear()}close(){this.endAttempt()}dispose(){this.close(),this.clear()}endAttempt(){this.attemptGeneration++,this.connected=!1,this.pendingHold?.end()}connectNow(){let e=Promise.resolve(this);return this.scheduleFirstDelivery(),e}holdNow(e,t,n){return new Promise((r,i)=>{let a=setTimeout(()=>this.pendingHold?.end(),n),o=()=>{clearTimeout(a),this.pendingHold=void 0};this.pendingHold={attempt:e,end:()=>{o(),r(this)},complete:()=>{o(),r(this),this.scheduleFirstDelivery()},fail:()=>{o();let e=this.fatalError(t);this.emitter.emit(`connectionError`,e),i(e)}}})}failNow(e){let t=this.fatalError(e);return this.emitter.emit(`connectionError`,t),Promise.reject(t)}fatalError(t){return new e.l(`Connection failed with status: 401. Error: the test client failed this ${t}. This is an unrecoverable error, will not attempt to reconnect.`,401)}scheduleFirstDelivery(){let e=this.attemptGeneration;Promise.resolve().then(()=>{}).then(()=>{e===this.attemptGeneration&&(this.connected=!0,this.deliver(this.fullUpdate()))})}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(`configSetReceived`,e)}catch(e){this.logger.error(`[InMemoryConnection] Error dispatching a config update from the test client: `,e)}}fullUpdate(){return{...u,kind:`full`,configs:Object.fromEntries(this.configs)}}store(e){this.encodeAll(e).forEach((e,t)=>this.configs.set(t,e))}encodeAll(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,l(n,r,++this.revision));return t}};const p={updateContext:async()=>{},evaluatedConfig:()=>{},recordInvalidContext:()=>{},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(`initialize`)}completeInitialization(){this.connection.complete(`initialize`)}failInitialization(){this.connection.fail(`initialize`)}holdContextUpdate(){this.connection.hold(`updateContext`)}completeContextUpdate(){this.connection.complete(`updateContext`)}failContextUpdate(){this.connection.fail(`updateContext`)}get contextUpdates(){return this.connection.contextUpdates}};const h=(t,n)=>{let r=n?.logger??e.s(),i=new f(r,n?.values??{});return new m(new e.r(p,`test-client`,t,{logger:r,connection:{timeout:n?.timeout}},{transport:i}),i)},g=t=>{let n=globalThis,r=n[e.t];return n[e.t]=t.client,()=>{n[e.t]=r}},_=e=>h({sdkName:`nextjs-sdk`,sdkVersion:`1.10.0`},e);exports.ConfigDirectorConnectionError=e.l,exports.ConfigDirectorValidationError=e.u,exports.createTestClient=_,exports.installTestClient=g;
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as ConfigDirectorLogger, n as ConfigDirectorClient, r as ConfigDirectorContext } from "./types-fL7X5zwg.cjs";
2
+ import { i as ConfigDirectorLogger, n as ConfigDirectorClient, r as ConfigDirectorContext } from "./types-Cz68hQ6f.cjs";
3
3
 
4
4
  //#region ../shared/src/errors.d.ts
5
5
  declare class ConfigDirectorConnectionError extends Error {
@@ -103,7 +103,8 @@ interface TestClient {
103
103
  failContextUpdate(): void;
104
104
  /**
105
105
  * The context of every `initialize` and `updateContext` call, in call order. `initialize` without
106
- * a context records an empty context. Resumes are not recorded.
106
+ * a context records an empty context, and so does a call whose context has an invalid kind, since
107
+ * the client connects without it. Resumes are not recorded.
107
108
  */
108
109
  readonly contextUpdates: ConfigDirectorContext[];
109
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.cts","names":[],"sources":["../../../shared/src/errors.ts","../../../shared/src/testing/values.ts","../../../js-client-core/src/testing/TestClient.ts","../../../js-client-core/src/testing/installTestClient.ts","../../src/client/testing.ts"],"mappings":";;;;cAAa,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;;;KCZF,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;;;;ADPxC;;KEYY,iBAAA;EFZ4C;;;;;EEkBtD,MAAA,GAAS,UAAA;EFdoB;;;AAQ/B;EEWE,OAAA;;;;EAIA,MAAA,GAAS,oBAAA;AAAA;;;;;;;ADxBX;;UCmCiB,UAAA;EDnC8F;;;;EAAA,SCwCpG,MAAA,EAAQ,oBAAA;EDxC4F;AAE/G;;;EC4CE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,SAAA;ED5CqB;;;;ECkDlD,WAAA,CAAY,GAAA;EDhDF;;;;ECsDV,aAAA,CAAc,MAAA,EAAQ,UAAA;;;;AAjDxB;EAuDE,kBAAA;;;;;EAMA,sBAAA;EA9CA;;;;EAoDA,kBAAA;EAzCyB;;;;EA+CzB,iBAAA;EAkByB;;;;EAZzB,qBAAA;EA1CA;;;;EAgDA,iBAAA;EA1CY;;;;EAAA,SAgDH,cAAA,EAAgB,qBAAA;AAAA;;;;;AFvG3B;;;;;;;;;;;;AAYA;;;;;;;cGYa,iBAAA,GAAqB,UAAA,EAAY,UAAA;;;;;;;;;;;;;;AHZ9C;;;;;;;;;cIiBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
1
+ {"version":3,"file":"testing.d.cts","names":[],"sources":["../../../shared/src/errors.ts","../../../shared/src/testing/values.ts","../../../js-client-core/src/testing/TestClient.ts","../../../js-client-core/src/testing/installTestClient.ts","../../src/client/testing.ts"],"mappings":";;;;cAAa,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;;;KCZF,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;;;;ADPxC;;KEYY,iBAAA;EFZ4C;;;;;EEkBtD,MAAA,GAAS,UAAA;EFdoB;;;AAQ/B;EEWE,OAAA;;;;EAIA,MAAA,GAAS,oBAAA;AAAA;;;;;;;ADxBX;;UCmCiB,UAAA;EDnC8F;;;;EAAA,SCwCpG,MAAA,EAAQ,oBAAA;EDxC4F;AAE/G;;;EC4CE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,SAAA;ED5CqB;;;;ECkDlD,WAAA,CAAY,GAAA;EDhDF;;;;ECsDV,aAAA,CAAc,MAAA,EAAQ,UAAA;;;;AAjDxB;EAuDE,kBAAA;;;;;EAMA,sBAAA;EA9CA;;;;EAoDA,kBAAA;EAzCyB;;;;EA+CzB,iBAAA;EAmByB;;;;EAbzB,qBAAA;EA1CA;;;;EAgDA,iBAAA;EA1CY;;;;;EAAA,SAiDH,cAAA,EAAgB,qBAAA;AAAA;;;;;AFxG3B;;;;;;;;;;;;AAYA;;;;;;;cGYa,iBAAA,GAAqB,UAAA,EAAY,UAAA;;;;;;;;;;;;;;AHZ9C;;;;;;;;;cIiBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as ConfigDirectorLogger, n as ConfigDirectorClient, r as ConfigDirectorContext } from "./types-DA2VZKNX.mjs";
2
+ import { i as ConfigDirectorLogger, n as ConfigDirectorClient, r as ConfigDirectorContext } from "./types-De4z0tu4.mjs";
3
3
 
4
4
  //#region ../shared/src/errors.d.ts
5
5
  declare class ConfigDirectorConnectionError extends Error {
@@ -103,7 +103,8 @@ interface TestClient {
103
103
  failContextUpdate(): void;
104
104
  /**
105
105
  * The context of every `initialize` and `updateContext` call, in call order. `initialize` without
106
- * a context records an empty context. Resumes are not recorded.
106
+ * a context records an empty context, and so does a call whose context has an invalid kind, since
107
+ * the client connects without it. Resumes are not recorded.
107
108
  */
108
109
  readonly contextUpdates: ConfigDirectorContext[];
109
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.mts","names":[],"sources":["../../../shared/src/errors.ts","../../../shared/src/testing/values.ts","../../../js-client-core/src/testing/TestClient.ts","../../../js-client-core/src/testing/installTestClient.ts","../../src/client/testing.ts"],"mappings":";;;;cAAa,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;;;KCZF,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;;;;ADPxC;;KEYY,iBAAA;EFZ4C;;;;;EEkBtD,MAAA,GAAS,UAAA;EFdoB;;;AAQ/B;EEWE,OAAA;;;;EAIA,MAAA,GAAS,oBAAA;AAAA;;;;;;;ADxBX;;UCmCiB,UAAA;EDnC8F;;;;EAAA,SCwCpG,MAAA,EAAQ,oBAAA;EDxC4F;AAE/G;;;EC4CE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,SAAA;ED5CqB;;;;ECkDlD,WAAA,CAAY,GAAA;EDhDF;;;;ECsDV,aAAA,CAAc,MAAA,EAAQ,UAAA;;;;AAjDxB;EAuDE,kBAAA;;;;;EAMA,sBAAA;EA9CA;;;;EAoDA,kBAAA;EAzCyB;;;;EA+CzB,iBAAA;EAkByB;;;;EAZzB,qBAAA;EA1CA;;;;EAgDA,iBAAA;EA1CY;;;;EAAA,SAgDH,cAAA,EAAgB,qBAAA;AAAA;;;;;AFvG3B;;;;;;;;;;;;AAYA;;;;;;;cGYa,iBAAA,GAAqB,UAAA,EAAY,UAAA;;;;;;;;;;;;;;AHZ9C;;;;;;;;;cIiBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
1
+ {"version":3,"file":"testing.d.mts","names":[],"sources":["../../../shared/src/errors.ts","../../../shared/src/testing/values.ts","../../../js-client-core/src/testing/TestClient.ts","../../../js-client-core/src/testing/installTestClient.ts","../../src/client/testing.ts"],"mappings":";;;;cAAa,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;;;KCZF,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;;;;ADPxC;;KEYY,iBAAA;EFZ4C;;;;;EEkBtD,MAAA,GAAS,UAAA;EFdoB;;;AAQ/B;EEWE,OAAA;;;;EAIA,MAAA,GAAS,oBAAA;AAAA;;;;;;;ADxBX;;UCmCiB,UAAA;EDnC8F;;;;EAAA,SCwCpG,MAAA,EAAQ,oBAAA;EDxC4F;AAE/G;;;EC4CE,QAAA,CAAS,GAAA,UAAa,KAAA,EAAO,SAAA;ED5CqB;;;;ECkDlD,WAAA,CAAY,GAAA;EDhDF;;;;ECsDV,aAAA,CAAc,MAAA,EAAQ,UAAA;;;;AAjDxB;EAuDE,kBAAA;;;;;EAMA,sBAAA;EA9CA;;;;EAoDA,kBAAA;EAzCyB;;;;EA+CzB,iBAAA;EAmByB;;;;EAbzB,qBAAA;EA1CA;;;;EAgDA,iBAAA;EA1CY;;;;;EAAA,SAiDH,cAAA,EAAgB,qBAAA;AAAA;;;;;AFxG3B;;;;;;;;;;;;AAYA;;;;;;;cGYa,iBAAA,GAAqB,UAAA,EAAY,UAAA;;;;;;;;;;;;;;AHZ9C;;;;;;;;;cIiBa,gBAAA,GAAoB,OAAA,GAAU,iBAAA,KAAoB,UAAA"}
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- "use client";import{d as e,f as t,l as n,r,s as i,t as a,u as o}from"./installedTestClient-CDdDsfVQ.mjs";const s=(t,n)=>{if(t.trim().length==0)throw new e(`Invalid config key. The key of a test value cannot be empty.`);return c(t,n)},c=(t,n)=>{switch(typeof n){case`boolean`:return{type:`boolean`,value:n?`true`:`false`};case`number`:return l(t,n);case`string`:return{type:`string`,value:n};case`object`:if(n!==null)return d(t,n,``),{type:`json`,value:JSON.stringify(n)}}throw new e(`Invalid test value for '${t}': ${m(n)}. Use a boolean, a number, a string, a plain object, or an array.`)},l=(t,n)=>{if(!Number.isFinite(n))throw new e(`Invalid test value for '${t}': ${m(n)} is not a finite number.`);if(Number.isInteger(n)){if(!Number.isSafeInteger(n))throw new e(`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:u(n)}},u=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}`},d=(e,t,n)=>{if(!(t===null||typeof t==`boolean`||typeof t==`string`)){if(typeof t==`number`){if(!Number.isFinite(t))throw f(e,n,`${m(t)} is not a finite number`);return}if(Array.isArray(t)){t.forEach((t,r)=>d(e,t,`${n}[${r}]`));return}if(p(t)){for(let[r,i]of Object.entries(t))d(e,i,n?`${n}.${r}`:r);return}throw f(e,n,`${m(t)} cannot be encoded as JSON`)}},f=(t,n,r)=>new e(`Invalid test value for '${t}'${n?` at '${n}'`:``}: ${r}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`),p=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},m=e=>{if(e===null)return`null`;if(typeof e==`function`)return`a function`;if(typeof e==`object`){var t,n;return`a ${(t=(n=e.constructor)==null?void 0:n.name)==null?`object`:t} instance`}return typeof e==`symbol`?`a symbol`:String(e)},h=(e,t,n)=>{let r=s(e,t);return{id:`test-config:${e}`,key:e,type:r.type,value:r.value,valueId:`test-value:${e}:${n}`}},g={environmentId:`test-environment`,projectId:`test-project`},_=e=>{switch(e){case`initialization`:return`initialize`;case`context update`:return`updateContext`;default:return}};var v=class{constructor(e,n){this.logger=e,this.emitter=new t,this.configs=new Map,this.recordedContexts=[],this.pendingDeliveries=[],this.armed={initialize:`nothing`,updateContext:`nothing`},this.connected=!1,this.delivering=!1,this.revision=0,this.attemptGeneration=0,this.on=(e,t)=>this.emitter.on(e,t),this.off=(e,t)=>this.emitter.off(e,t),this.store(n)}get contextUpdates(){return[...this.recordedContexts]}setValue(e,t){let r=h(e,t,++this.revision);this.configs.set(e,r),this.deliverIfConnected(n(n({},g),{},{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.initialize=`nothing`,this.armed.updateContext=`nothing`,this.deliverIfConnected(this.fullUpdate())}hold(e){this.armed[e]=`hold`}complete(e){var t;((t=this.pendingHold)==null?void 0:t.attempt)===e?this.pendingHold.complete():this.armed[e]===`hold`&&(this.armed[e]=`nothing`)}fail(e){var t;((t=this.pendingHold)==null?void 0:t.attempt)===e?this.pendingHold.fail():this.armed[e]=`failure`}connect(e,t,n){this.endAttempt();let r=_(n);if(!r)return this.connectNow();this.recordedContexts.push(e);let i=this.armed[r];switch(this.armed[r]=`nothing`,i){case`failure`:return this.failNow(n);case`hold`:return this.holdNow(r,n,t);default:return this.connectNow()}}clear(){this.emitter.clear()}close(){this.endAttempt()}dispose(){this.close(),this.clear()}endAttempt(){var e;this.attemptGeneration++,this.connected=!1,(e=this.pendingHold)==null||e.end()}connectNow(){let e=Promise.resolve(this);return this.scheduleFirstDelivery(),e}holdNow(e,t,n){return new Promise((r,i)=>{let a=setTimeout(()=>{var e;return(e=this.pendingHold)==null?void 0:e.end()},n),o=()=>{clearTimeout(a),this.pendingHold=void 0};this.pendingHold={attempt:e,end:()=>{o(),r(this)},complete:()=>{o(),r(this),this.scheduleFirstDelivery()},fail:()=>{o();let e=this.fatalError(t);this.emitter.emit(`connectionError`,e),i(e)}}})}failNow(e){let t=this.fatalError(e);return this.emitter.emit(`connectionError`,t),Promise.reject(t)}fatalError(e){return new o(`Connection failed with status: 401. Error: the test client failed this ${e}. This is an unrecoverable error, will not attempt to reconnect.`,401)}scheduleFirstDelivery(){let e=this.attemptGeneration;Promise.resolve().then(()=>{}).then(()=>{e===this.attemptGeneration&&(this.connected=!0,this.deliver(this.fullUpdate()))})}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(`configSetReceived`,e)}catch(e){this.logger.error(`[InMemoryConnection] Error dispatching a config update from the test client: `,e)}}fullUpdate(){return n(n({},g),{},{kind:`full`,configs:Object.fromEntries(this.configs)})}store(e){this.encodeAll(e).forEach((e,t)=>this.configs.set(t,e))}encodeAll(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,h(n,r,++this.revision));return t}};const y={updateContext:async()=>{},evaluatedConfig:()=>{},close:async()=>{}};var b=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(`initialize`)}completeInitialization(){this.connection.complete(`initialize`)}failInitialization(){this.connection.fail(`initialize`)}holdContextUpdate(){this.connection.hold(`updateContext`)}completeContextUpdate(){this.connection.complete(`updateContext`)}failContextUpdate(){this.connection.fail(`updateContext`)}get contextUpdates(){return this.connection.contextUpdates}};const x=(e,t)=>{var n,a;let o=(n=t==null?void 0:t.logger)==null?i():n,s=new v(o,(a=t==null?void 0:t.values)==null?{}:a);return new b(new r(y,`test-client`,e,{logger:o,connection:{timeout:t==null?void 0:t.timeout}},{transport:s}),s)},S=e=>{let t=globalThis,n=t[a];return t[a]=e.client,()=>{t[a]=n}},C=e=>x({sdkName:`nextjs-sdk`,sdkVersion:`1.9.1`},e);export{o as ConfigDirectorConnectionError,e as ConfigDirectorValidationError,C as createTestClient,S as installTestClient};
2
+ "use client";import{d as e,f as t,l as n,r,s as i,t as a,u as o}from"./installedTestClient-CpjJvHZD.mjs";const s=(t,n)=>{if(t.trim().length==0)throw new e(`Invalid config key. The key of a test value cannot be empty.`);return c(t,n)},c=(t,n)=>{switch(typeof n){case`boolean`:return{type:`boolean`,value:n?`true`:`false`};case`number`:return l(t,n);case`string`:return{type:`string`,value:n};case`object`:if(n!==null)return d(t,n,``),{type:`json`,value:JSON.stringify(n)}}throw new e(`Invalid test value for '${t}': ${m(n)}. Use a boolean, a number, a string, a plain object, or an array.`)},l=(t,n)=>{if(!Number.isFinite(n))throw new e(`Invalid test value for '${t}': ${m(n)} is not a finite number.`);if(Number.isInteger(n)){if(!Number.isSafeInteger(n))throw new e(`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:u(n)}},u=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}`},d=(e,t,n)=>{if(!(t===null||typeof t==`boolean`||typeof t==`string`)){if(typeof t==`number`){if(!Number.isFinite(t))throw f(e,n,`${m(t)} is not a finite number`);return}if(Array.isArray(t)){t.forEach((t,r)=>d(e,t,`${n}[${r}]`));return}if(p(t)){for(let[r,i]of Object.entries(t))d(e,i,n?`${n}.${r}`:r);return}throw f(e,n,`${m(t)} cannot be encoded as JSON`)}},f=(t,n,r)=>new e(`Invalid test value for '${t}'${n?` at '${n}'`:``}: ${r}. JSON contents can hold booleans, finite numbers, strings, null, arrays, and plain objects.`),p=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},m=e=>{if(e===null)return`null`;if(typeof e==`function`)return`a function`;if(typeof e==`object`){var t,n;return`a ${(t=(n=e.constructor)==null?void 0:n.name)==null?`object`:t} instance`}return typeof e==`symbol`?`a symbol`:String(e)},h=(e,t,n)=>{let r=s(e,t);return{id:`test-config:${e}`,key:e,type:r.type,value:r.value,valueId:`test-value:${e}:${n}`}},g={environmentId:`test-environment`,projectId:`test-project`},_=e=>{switch(e){case`initialization`:return`initialize`;case`context update`:return`updateContext`;default:return}};var v=class{constructor(e,n){this.logger=e,this.emitter=new t,this.configs=new Map,this.recordedContexts=[],this.pendingDeliveries=[],this.armed={initialize:`nothing`,updateContext:`nothing`},this.connected=!1,this.delivering=!1,this.revision=0,this.attemptGeneration=0,this.on=(e,t)=>this.emitter.on(e,t),this.off=(e,t)=>this.emitter.off(e,t),this.store(n)}get contextUpdates(){return[...this.recordedContexts]}setValue(e,t){let r=h(e,t,++this.revision);this.configs.set(e,r),this.deliverIfConnected(n(n({},g),{},{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.initialize=`nothing`,this.armed.updateContext=`nothing`,this.deliverIfConnected(this.fullUpdate())}hold(e){this.armed[e]=`hold`}complete(e){var t;((t=this.pendingHold)==null?void 0:t.attempt)===e?this.pendingHold.complete():this.armed[e]===`hold`&&(this.armed[e]=`nothing`)}fail(e){var t;((t=this.pendingHold)==null?void 0:t.attempt)===e?this.pendingHold.fail():this.armed[e]=`failure`}connect(e,t,n){this.endAttempt();let r=_(n);if(!r)return this.connectNow();this.recordedContexts.push(e);let i=this.armed[r];switch(this.armed[r]=`nothing`,i){case`failure`:return this.failNow(n);case`hold`:return this.holdNow(r,n,t);default:return this.connectNow()}}clear(){this.emitter.clear()}close(){this.endAttempt()}dispose(){this.close(),this.clear()}endAttempt(){var e;this.attemptGeneration++,this.connected=!1,(e=this.pendingHold)==null||e.end()}connectNow(){let e=Promise.resolve(this);return this.scheduleFirstDelivery(),e}holdNow(e,t,n){return new Promise((r,i)=>{let a=setTimeout(()=>{var e;return(e=this.pendingHold)==null?void 0:e.end()},n),o=()=>{clearTimeout(a),this.pendingHold=void 0};this.pendingHold={attempt:e,end:()=>{o(),r(this)},complete:()=>{o(),r(this),this.scheduleFirstDelivery()},fail:()=>{o();let e=this.fatalError(t);this.emitter.emit(`connectionError`,e),i(e)}}})}failNow(e){let t=this.fatalError(e);return this.emitter.emit(`connectionError`,t),Promise.reject(t)}fatalError(e){return new o(`Connection failed with status: 401. Error: the test client failed this ${e}. This is an unrecoverable error, will not attempt to reconnect.`,401)}scheduleFirstDelivery(){let e=this.attemptGeneration;Promise.resolve().then(()=>{}).then(()=>{e===this.attemptGeneration&&(this.connected=!0,this.deliver(this.fullUpdate()))})}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(`configSetReceived`,e)}catch(e){this.logger.error(`[InMemoryConnection] Error dispatching a config update from the test client: `,e)}}fullUpdate(){return n(n({},g),{},{kind:`full`,configs:Object.fromEntries(this.configs)})}store(e){this.encodeAll(e).forEach((e,t)=>this.configs.set(t,e))}encodeAll(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,h(n,r,++this.revision));return t}};const y={updateContext:async()=>{},evaluatedConfig:()=>{},recordInvalidContext:()=>{},close:async()=>{}};var b=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(`initialize`)}completeInitialization(){this.connection.complete(`initialize`)}failInitialization(){this.connection.fail(`initialize`)}holdContextUpdate(){this.connection.hold(`updateContext`)}completeContextUpdate(){this.connection.complete(`updateContext`)}failContextUpdate(){this.connection.fail(`updateContext`)}get contextUpdates(){return this.connection.contextUpdates}};const x=(e,t)=>{var n,a;let o=(n=t==null?void 0:t.logger)==null?i():n,s=new v(o,(a=t==null?void 0:t.values)==null?{}:a);return new b(new r(y,`test-client`,e,{logger:o,connection:{timeout:t==null?void 0:t.timeout}},{transport:s}),s)},S=e=>{let t=globalThis,n=t[a];return t[a]=e.client,()=>{t[a]=n}},C=e=>x({sdkName:`nextjs-sdk`,sdkVersion:`1.10.0`},e);export{o as ConfigDirectorConnectionError,e as ConfigDirectorValidationError,C as createTestClient,S as installTestClient};
3
3
  //# sourceMappingURL=testing.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.mjs","names":["encodeTestValue","encodeShared","createTestClient"],"sources":["../../../shared/src/testing/values.ts","../../../js-client-core/src/testing/values.ts","../../../js-client-core/src/testing/InMemoryConnection.ts","../../../js-client-core/src/testing/TestClient.ts","../../../js-client-core/src/testing/installTestClient.ts","../../src/client/testing.ts"],"sourcesContent":["import type { ConfigType } from \"../types\";\nimport { ConfigDirectorValidationError } from \"../errors\";\n\nexport type TestJsonValue = boolean | number | string | null | TestJsonValue[] | { [key: string]: TestJsonValue };\n\nexport type TestValue = boolean | number | string | TestJsonValue[] | { [key: string]: TestJsonValue };\n\nexport type TestValues = Record<string, TestValue>;\n\nexport type EncodedTestValue = { type: ConfigType; value: string };\n\nexport const encodeTestValue = (key: string, value: TestValue): EncodedTestValue => {\n if (key.trim().length == 0) {\n throw new ConfigDirectorValidationError(\"Invalid config key. The key of a test value cannot be empty.\");\n }\n return encode(key, value);\n};\n\nconst encode = (key: string, value: TestValue): EncodedTestValue => {\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 test 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): EncodedTestValue => {\n if (!Number.isFinite(value)) {\n throw new ConfigDirectorValidationError(`Invalid test 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 test 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 test 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 { ConfigState } from \"../types\";\nimport { encodeTestValue as encodeShared } from \"@shared/testing/values\";\nimport type { TestValue } from \"@shared/testing/values\";\n\nexport type { TestJsonValue, TestValue, TestValues } from \"@shared/testing/values\";\n\nexport const encodeTestValue = (key: string, value: TestValue, revision: number): ConfigState => {\n const encoded = encodeShared(key, value);\n return {\n id: `test-config:${key}`,\n key,\n type: encoded.type,\n value: encoded.value,\n valueId: `test-value:${key}:${revision}`,\n };\n};\n","import type {\n ClientConnectAction,\n ConfigDirectorContext,\n ConfigDirectorLogger,\n ConfigSet,\n ConfigState,\n Transport,\n TransportEvents,\n} from \"../types\";\nimport { Emitter } from \"../Emitter\";\nimport { ConfigDirectorConnectionError } from \"../errors\";\nimport { encodeTestValue, type TestValue, type TestValues } from \"./values\";\n\ntype ControlledAttempt = \"initialize\" | \"updateContext\";\n\ntype Armed = \"nothing\" | \"hold\" | \"failure\";\n\ntype PendingHold = {\n attempt: ControlledAttempt;\n end: () => void;\n complete: () => void;\n fail: () => void;\n};\n\nconst TEST_ENVIRONMENT = { environmentId: \"test-environment\", projectId: \"test-project\" };\n\nconst controlledAttemptFor = (reason: ClientConnectAction): ControlledAttempt | undefined => {\n switch (reason) {\n case \"initialization\":\n return \"initialize\";\n case \"context update\":\n return \"updateContext\";\n default:\n return undefined;\n }\n};\n\nexport class InMemoryConnection implements Transport {\n private readonly emitter = new Emitter<TransportEvents>();\n private readonly configs = new Map<string, ConfigState>();\n private readonly recordedContexts: ConfigDirectorContext[] = [];\n private readonly pendingDeliveries: ConfigSet[] = [];\n private readonly armed: Record<ControlledAttempt, Armed> = { initialize: \"nothing\", updateContext: \"nothing\" };\n private pendingHold: PendingHold | undefined;\n private connected = false;\n private delivering = false;\n private revision = 0;\n private attemptGeneration = 0;\n\n constructor(\n private readonly logger: ConfigDirectorLogger,\n values: TestValues,\n ) {\n this.store(values);\n }\n\n public get contextUpdates(): ConfigDirectorContext[] {\n return [...this.recordedContexts];\n }\n\n public setValue(key: string, value: TestValue) {\n const configState = encodeTestValue(key, value, ++this.revision);\n this.configs.set(key, configState);\n this.deliverIfConnected({ ...TEST_ENVIRONMENT, kind: \"delta\", configs: { [key]: configState } });\n }\n\n public removeValue(key: string) {\n this.configs.delete(key);\n this.deliverIfConnected(this.fullUpdate());\n }\n\n public replaceValues(values: TestValues) {\n const encoded = this.encodeAll(values);\n this.configs.clear();\n encoded.forEach((configState, key) => this.configs.set(key, configState));\n this.armed.initialize = \"nothing\";\n this.armed.updateContext = \"nothing\";\n this.deliverIfConnected(this.fullUpdate());\n }\n\n public hold(attempt: ControlledAttempt) {\n this.armed[attempt] = \"hold\";\n }\n\n public complete(attempt: ControlledAttempt) {\n if (this.pendingHold?.attempt === attempt) {\n this.pendingHold.complete();\n } else if (this.armed[attempt] === \"hold\") {\n this.armed[attempt] = \"nothing\";\n }\n }\n\n public fail(attempt: ControlledAttempt) {\n if (this.pendingHold?.attempt === attempt) {\n this.pendingHold.fail();\n } else {\n this.armed[attempt] = \"failure\";\n }\n }\n\n public connect(context: ConfigDirectorContext, timeout: number, reason: ClientConnectAction): Promise<this> {\n this.endAttempt();\n const attempt = controlledAttemptFor(reason);\n if (!attempt) {\n return this.connectNow();\n }\n this.recordedContexts.push(context);\n const armed = this.armed[attempt];\n this.armed[attempt] = \"nothing\";\n switch (armed) {\n case \"failure\":\n return this.failNow(reason);\n case \"hold\":\n return this.holdNow(attempt, reason, timeout);\n default:\n return this.connectNow();\n }\n }\n\n public on: Transport[\"on\"] = (name, handler) => this.emitter.on(name, handler);\n\n public off: Transport[\"off\"] = (name, handler) => this.emitter.off(name, handler);\n\n public clear() {\n this.emitter.clear();\n }\n\n public close() {\n this.endAttempt();\n }\n\n public dispose() {\n this.close();\n this.clear();\n }\n\n private endAttempt() {\n this.attemptGeneration++;\n this.connected = false;\n this.pendingHold?.end();\n }\n\n private connectNow(): Promise<this> {\n const connected = Promise.resolve(this);\n this.scheduleFirstDelivery();\n return connected;\n }\n\n private holdNow(attempt: ControlledAttempt, reason: ClientConnectAction, timeout: number): Promise<this> {\n return new Promise<this>((resolve, reject) => {\n const timer = setTimeout(() => this.pendingHold?.end(), timeout);\n const release = () => {\n clearTimeout(timer);\n this.pendingHold = undefined;\n };\n this.pendingHold = {\n attempt,\n end: () => {\n release();\n resolve(this);\n },\n complete: () => {\n release();\n resolve(this);\n this.scheduleFirstDelivery();\n },\n fail: () => {\n release();\n const error = this.fatalError(reason);\n this.emitter.emit(\"connectionError\", error);\n reject(error);\n },\n };\n });\n }\n\n private failNow(reason: ClientConnectAction): Promise<this> {\n const error = this.fatalError(reason);\n this.emitter.emit(\"connectionError\", error);\n return Promise.reject(error);\n }\n\n private fatalError(reason: ClientConnectAction) {\n return new ConfigDirectorConnectionError(\n `Connection failed with status: 401. Error: the test client failed this ${reason}. This is an unrecoverable error, will not attempt to reconnect.`,\n 401,\n );\n }\n\n private scheduleFirstDelivery() {\n const generation = this.attemptGeneration;\n void Promise.resolve()\n .then(() => {})\n .then(() => {\n if (generation !== this.attemptGeneration) {\n return;\n }\n this.connected = true;\n this.deliver(this.fullUpdate());\n });\n }\n\n private deliverIfConnected(configSet: ConfigSet) {\n if (this.connected) {\n this.deliver(configSet);\n }\n }\n\n private deliver(configSet: ConfigSet) {\n this.pendingDeliveries.push(configSet);\n if (this.delivering) {\n return;\n }\n this.delivering = true;\n try {\n let next = this.pendingDeliveries.shift();\n while (next) {\n this.emitCaught(next);\n next = this.pendingDeliveries.shift();\n }\n } finally {\n this.delivering = false;\n }\n }\n\n private emitCaught(configSet: ConfigSet) {\n try {\n this.emitter.emit(\"configSetReceived\", configSet);\n } catch (error) {\n this.logger.error(\"[InMemoryConnection] Error dispatching a config update from the test client: \", error);\n }\n }\n\n private fullUpdate(): ConfigSet {\n return { ...TEST_ENVIRONMENT, kind: \"full\", configs: Object.fromEntries(this.configs) };\n }\n\n private store(values: TestValues) {\n this.encodeAll(values).forEach((configState, key) => this.configs.set(key, configState));\n }\n\n private encodeAll(values: TestValues): Map<string, ConfigState> {\n const encoded = new Map<string, ConfigState>();\n for (const [key, value] of Object.entries(values)) {\n encoded.set(key, encodeTestValue(key, value, ++this.revision));\n }\n return encoded;\n }\n}\n","import { DefaultConfigDirectorClient } from \"../DefaultConfigDirectorClient\";\nimport { createDefaultLogger } from \"../logger\";\nimport type { ConfigDirectorClient, ConfigDirectorContext, ConfigDirectorLogger, IdentifyingSdkOptions } from \"../types\";\nimport type { TelemetryClient } from \"../telemetry\";\nimport { InMemoryConnection } from \"./InMemoryConnection\";\nimport type { TestValue, TestValues } from \"./values\";\n\nconst TEST_CLIENT_SDK_KEY = \"test-client\";\n\n/**\n * Options for {@link createTestClient}. Every option is optional.\n */\nexport type TestClientOptions = {\n /**\n * The config values the test client serves once its client connects, keyed by config key. The\n * config type follows from each value: a boolean, an integral number (`integer`), any other\n * number (`float`), a string, or a plain object or array (`json`).\n */\n values?: TestValues;\n /**\n * The connection timeout of the SDK client, in milliseconds. It bounds how long a held\n * `initialize` or `updateContext` waits. Defaults to the SDK's production timeout.\n */\n timeout?: number;\n /**\n * The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level.\n */\n logger?: ConfigDirectorLogger;\n};\n\n/**\n * A real ConfigDirector client connected to an in-memory server that the test controls.\n *\n * The client under {@link TestClient.client} is the SDK's production client: value parsing, watchers,\n * events, and readiness behave as they do against ConfigDirector. Only the network connection and\n * telemetry are replaced, so no request is ever made and nothing is left running after the client is\n * disposed.\n */\nexport interface TestClient {\n /**\n * The SDK client to hand to the code under test. It starts uninitialized, like a production\n * client; `initialize` completes at once with the seeded values unless initialization is held.\n */\n readonly client: ConfigDirectorClient;\n\n /**\n * Stores `value` under `key` and, when the client is connected, delivers it as an update, so\n * reads, watchers, and `configsUpdated` handlers see it.\n */\n setValue(key: string, value: TestValue): void;\n\n /**\n * Removes `key` and, when the client is connected, delivers a full update without it, so reads\n * fall back to the in-code default value and watchers of `key` receive that default.\n */\n removeValue(key: string): void;\n\n /**\n * Replaces every stored value with `values`, disarms any armed hold or failure, and, when the\n * client is connected, delivers the new values as a full update.\n */\n replaceValues(values: TestValues): void;\n\n /**\n * Makes the next `initialize` wait until {@link completeInitialization} or\n * {@link failInitialization} is called, or until the client's timeout elapses.\n */\n holdInitialization(): void;\n\n /**\n * Delivers the stored values to a held `initialize`, so it completes with the client ready. When\n * a hold is armed but no `initialize` has started, it disarms the hold instead.\n */\n completeInitialization(): void;\n\n /**\n * Fails a held `initialize` with an unrecoverable connection error, or arms the next `initialize`\n * to fail. `initialize` completes promptly with the client not ready and `connectionError` fires.\n */\n failInitialization(): void;\n\n /**\n * Makes the next `updateContext` wait until {@link completeContextUpdate} or\n * {@link failContextUpdate} is called, or until the client's timeout elapses.\n */\n holdContextUpdate(): void;\n\n /**\n * Delivers the stored values to a held `updateContext`, or disarms an armed hold that no\n * `updateContext` has picked up.\n */\n completeContextUpdate(): void;\n\n /**\n * Fails a held `updateContext` with an unrecoverable connection error, or arms the next\n * `updateContext` to fail.\n */\n failContextUpdate(): void;\n\n /**\n * The context of every `initialize` and `updateContext` call, in call order. `initialize` without\n * a context records an empty context. Resumes are not recorded.\n */\n readonly contextUpdates: ConfigDirectorContext[];\n}\n\nconst noopTelemetryClient: TelemetryClient = {\n updateContext: async () => {},\n evaluatedConfig: () => {},\n close: async () => {},\n};\n\nclass InMemoryTestClient implements TestClient {\n constructor(\n public readonly client: ConfigDirectorClient,\n private readonly connection: InMemoryConnection,\n ) {}\n\n public setValue(key: string, value: TestValue) {\n this.connection.setValue(key, value);\n }\n\n public removeValue(key: string) {\n this.connection.removeValue(key);\n }\n\n public replaceValues(values: TestValues) {\n this.connection.replaceValues(values);\n }\n\n public holdInitialization() {\n this.connection.hold(\"initialize\");\n }\n\n public completeInitialization() {\n this.connection.complete(\"initialize\");\n }\n\n public failInitialization() {\n this.connection.fail(\"initialize\");\n }\n\n public holdContextUpdate() {\n this.connection.hold(\"updateContext\");\n }\n\n public completeContextUpdate() {\n this.connection.complete(\"updateContext\");\n }\n\n public failContextUpdate() {\n this.connection.fail(\"updateContext\");\n }\n\n public get contextUpdates(): ConfigDirectorContext[] {\n return this.connection.contextUpdates;\n }\n}\n\nexport const createTestClient = (sdkOptions: IdentifyingSdkOptions, options?: TestClientOptions): TestClient => {\n const logger = options?.logger ?? createDefaultLogger();\n const connection = new InMemoryConnection(logger, options?.values ?? {});\n const client = new DefaultConfigDirectorClient(\n noopTelemetryClient,\n TEST_CLIENT_SDK_KEY,\n sdkOptions,\n { logger, connection: { timeout: options?.timeout } },\n { transport: connection },\n );\n return new InMemoryTestClient(client, connection);\n};\n","import { INSTALLED_TEST_CLIENT_SLOT, type InstalledTestClientSlot } from \"../installedTestClient\";\nimport type { TestClient } from \"./TestClient\";\n\n/**\n * Makes every `ConfigDirectorProvider` mounted without a `client` prop, and every client\n * `withProvider` would build, use `testClient.client` instead of building a client, until the\n * returned function uninstalls it. Construction props such as `sdkKey` are ignored while installed;\n * `context` and `hooks` apply to the installed client as they do for a `client` prop. A provider\n * reads the installed client when it is constructed, so install before rendering.\n *\n * Installing again replaces the installed test client. Each uninstall function restores the test\n * client that was installed when it was created, so nested installs are uninstalled in reverse order.\n *\n * @param testClient The test client whose `client` providers should use\n * @returns A function that uninstalls it\n *\n * @example\n * let uninstallTestClient: () => void;\n * beforeEach(() => {\n * testClient = createTestClient({ values: { \"new-checkout\": true } });\n * uninstallTestClient = installTestClient(testClient);\n * });\n * afterEach(() => uninstallTestClient());\n */\nexport const installTestClient = (testClient: TestClient): (() => void) => {\n const slot = globalThis as InstalledTestClientSlot;\n const previous = slot[INSTALLED_TEST_CLIENT_SLOT];\n slot[INSTALLED_TEST_CLIENT_SLOT] = testClient.client;\n return () => {\n slot[INSTALLED_TEST_CLIENT_SLOT] = previous;\n };\n};\n","\"use client\";\n\nimport { createTestClient as createCoreTestClient } from \"@js-client-core/testing/index\";\nimport type { TestClient, TestClientOptions } from \"@js-client-core/testing/index\";\n\nexport { installTestClient } from \"@js-client-core/testing/index\";\nexport type { TestClient, TestClientOptions, TestValue, TestValues, TestJsonValue } from \"@js-client-core/testing/index\";\nexport { ConfigDirectorConnectionError, ConfigDirectorValidationError } from \"@shared/errors\";\n\n/**\n * Creates a {@link TestClient}: a real browser `ConfigDirectorClient` connected to an in-memory\n * server that the test controls. No network connection is opened and no telemetry is sent. Render\n * the client `ConfigDirectorProvider` with `client={testClient.client}`, or install it with\n * `installTestClient` for components that render their own provider.\n *\n * @param options {@link TestClientOptions}: the initial values, the client's timeout, and its logger\n * @returns A {@link TestClient} whose `client` is passed to the code under test\n *\n * @example\n * import { createTestClient } from \"@configdirector/nextjs-sdk/client/testing\";\n *\n * const testClient = createTestClient({ values: { \"new-checkout\": true } });\n * render(\n * <ConfigDirectorProvider client={testClient.client}>\n * <Checkout />\n * </ConfigDirectorProvider>,\n * );\n * act(() => testClient.setValue(\"new-checkout\", false));\n */\nexport const createTestClient = (options?: TestClientOptions): TestClient => {\n return createCoreTestClient({ sdkName: \"nextjs-sdk\", sdkVersion: \"__VERSION__\" }, options);\n};\n"],"mappings":";yGAWA,MAAaA,GAAmB,EAAa,IAAuC,CAClF,GAAI,EAAI,MAAM,CAAC,QAAU,EACvB,MAAM,IAAI,EAA8B,+DAA+D,CAEzG,OAAO,EAAO,EAAK,EAAM,EAGrB,GAAU,EAAa,IAAuC,CAClE,OAAQ,OAAO,EAAf,CACE,IAAK,UACH,MAAO,CAAE,KAAM,UAAW,MAAO,EAAQ,OAAS,QAAS,CAC7D,IAAK,SACH,OAAO,EAAa,EAAK,EAAM,CACjC,IAAK,SACH,MAAO,CAAE,KAAM,SAAU,QAAO,CAClC,IAAK,SACH,GAAI,IAAU,KAEZ,OADA,EAAmB,EAAK,EAAO,GAAG,CAC3B,CAAE,KAAM,OAAQ,MAAO,KAAK,UAAU,EAAM,CAAE,CAG3D,MAAM,IAAI,EACR,2BAA2B,EAAI,KAAK,EAAS,EAAM,CAAC,mEACrD,EAGG,GAAgB,EAAa,IAAoC,CACrE,GAAI,CAAC,OAAO,SAAS,EAAM,CACzB,MAAM,IAAI,EAA8B,2BAA2B,EAAI,KAAK,EAAS,EAAM,CAAC,0BAA0B,CAExH,GAAI,OAAO,UAAU,EAAM,CAAE,CAC3B,GAAI,CAAC,OAAO,cAAc,EAAM,CAC9B,MAAM,IAAI,EACR,2BAA2B,EAAI,KAAK,EAAM,oEAC3C,CAEH,MAAO,CAAE,KAAM,UAAW,MAAO,OAAO,EAAM,CAAE,CAElD,MAAO,CAAE,KAAM,QAAS,MAAO,EAAe,EAAM,CAAE,EAGlD,EAAkB,GAA0B,CAChD,IAAM,EAAO,OAAO,EAAM,CACpB,EAAgB,EAAK,QAAQ,KAAK,CACxC,GAAI,EAAgB,EAClB,OAAO,EAET,IAAM,EAAO,EAAK,WAAW,IAAI,CAAG,IAAM,GACpC,CAAC,EAAgB,GAAI,EAAiB,IAAM,EAAK,MAAM,EAAK,OAAQ,EAAc,CAAC,MAAM,IAAI,CAC7F,EAAe,OAAO,EAAK,MAAM,EAAgB,EAAE,CAAC,CAAG,EAAc,OAC3E,MAAO,GAAG,EAAK,IAAI,IAAI,OAAO,EAAa,GAAG,IAAgB,KAG1D,GAAsB,EAAa,EAAgB,IAAuB,CAC1E,SAAU,MAAQ,OAAO,GAAU,WAAa,OAAO,GAAU,UAGrE,IAAI,OAAO,GAAU,SAAU,CAC7B,GAAI,CAAC,OAAO,SAAS,EAAM,CACzB,MAAM,EAAoB,EAAK,EAAM,GAAG,EAAS,EAAM,CAAC,yBAAyB,CAEnF,OAEF,GAAI,MAAM,QAAQ,EAAM,CAAE,CACxB,EAAM,SAAS,EAAM,IAAU,EAAmB,EAAK,EAAM,GAAG,EAAK,GAAG,EAAM,GAAG,CAAC,CAClF,OAEF,GAAI,EAAc,EAAM,CAAE,CACxB,IAAK,GAAM,CAAC,EAAU,KAAS,OAAO,QAAQ,EAAM,CAClD,EAAmB,EAAK,EAAM,EAAO,GAAG,EAAK,GAAG,IAAa,EAAS,CAExE,OAEF,MAAM,EAAoB,EAAK,EAAM,GAAG,EAAS,EAAM,CAAC,4BAA4B,GAGhF,GAAuB,EAAa,EAAc,IACtD,IAAI,EACF,2BAA2B,EAAI,GAAG,EAAO,QAAQ,EAAK,GAAK,GAAG,IAAI,EAAQ,8FAC3E,CAEG,EAAiB,GAAqD,CAC1E,GAAI,OAAO,GAAU,WAAY,EAC/B,MAAO,GAET,IAAM,EAAY,OAAO,eAAe,EAAM,CAC9C,OAAO,IAAc,OAAO,WAAa,IAAc,MAGnD,EAAY,GAA2B,CAC3C,GAAI,IAAU,KACZ,MAAO,OAET,GAAI,OAAO,GAAU,WACnB,MAAO,aAET,GAAI,OAAO,GAAU,SAAU,SAC7B,MAAO,MAAA,GAAA,EAAK,EAAM,cAAA,KAAA,IAAA,GAAA,EAAa,OAAA,KAAQ,SAAR,EAAiB,WAKlD,OAHI,OAAO,GAAU,SACZ,WAEF,OAAO,EAAM,EC3GT,GAAmB,EAAa,EAAkB,IAAkC,CAC/F,IAAM,EAAUC,EAAa,EAAK,EAAM,CACxC,MAAO,CACL,GAAI,eAAe,IACnB,MACA,KAAM,EAAQ,KACd,MAAO,EAAQ,MACf,QAAS,cAAc,EAAI,GAAG,IAC/B,ECUG,EAAmB,CAAE,cAAe,mBAAoB,UAAW,eAAgB,CAEnF,EAAwB,GAA+D,CAC3F,OAAQ,EAAR,CACE,IAAK,iBACH,MAAO,aACT,IAAK,iBACH,MAAO,gBACT,QACE,SAIN,IAAa,EAAb,KAAqD,CAYnD,YACE,EACA,EACA,CAFiB,KAAA,OAAA,EAZnB,KAAiB,QAAU,IAAI,EAC/B,KAAiB,QAAU,IAAI,IAC/B,KAAiB,iBAA4C,EAAE,CAC/D,KAAiB,kBAAiC,EAAE,CACpD,KAAiB,MAA0C,CAAE,WAAY,UAAW,cAAe,UAAW,CAE9G,KAAQ,UAAY,GACpB,KAAQ,WAAa,GACrB,KAAQ,SAAW,EACnB,KAAQ,kBAAoB,EAwE5B,KAAO,IAAuB,EAAM,IAAY,KAAK,QAAQ,GAAG,EAAM,EAAQ,CAE9E,KAAO,KAAyB,EAAM,IAAY,KAAK,QAAQ,IAAI,EAAM,EAAQ,CApE/E,KAAK,MAAM,EAAO,CAGpB,IAAW,gBAA0C,CACnD,MAAO,CAAC,GAAG,KAAK,iBAAiB,CAGnC,SAAgB,EAAa,EAAkB,CAC7C,IAAM,EAAc,EAAgB,EAAK,EAAO,EAAE,KAAK,SAAS,CAChE,KAAK,QAAQ,IAAI,EAAK,EAAY,CAClC,KAAK,mBAAA,EAAA,EAAA,EAAA,CAAwB,EAAA,CAAA,EAAA,CAAA,CAAkB,KAAM,QAAS,QAAS,EAAG,GAAM,EAAa,EAAE,CAAC,CAGlG,YAAmB,EAAa,CAC9B,KAAK,QAAQ,OAAO,EAAI,CACxB,KAAK,mBAAmB,KAAK,YAAY,CAAC,CAG5C,cAAqB,EAAoB,CACvC,IAAM,EAAU,KAAK,UAAU,EAAO,CACtC,KAAK,QAAQ,OAAO,CACpB,EAAQ,SAAS,EAAa,IAAQ,KAAK,QAAQ,IAAI,EAAK,EAAY,CAAC,CACzE,KAAK,MAAM,WAAa,UACxB,KAAK,MAAM,cAAgB,UAC3B,KAAK,mBAAmB,KAAK,YAAY,CAAC,CAG5C,KAAY,EAA4B,CACtC,KAAK,MAAM,GAAW,OAGxB,SAAgB,EAA4B,SAC1C,EAAI,KAAK,cAAA,KAAA,IAAA,GAAA,EAAa,WAAY,EAChC,KAAK,YAAY,UAAU,CAClB,KAAK,MAAM,KAAa,SACjC,KAAK,MAAM,GAAW,WAI1B,KAAY,EAA4B,SACtC,EAAI,KAAK,cAAA,KAAA,IAAA,GAAA,EAAa,WAAY,EAChC,KAAK,YAAY,MAAM,CAEvB,KAAK,MAAM,GAAW,UAI1B,QAAe,EAAgC,EAAiB,EAA4C,CAC1G,KAAK,YAAY,CACjB,IAAM,EAAU,EAAqB,EAAO,CAC5C,GAAI,CAAC,EACH,OAAO,KAAK,YAAY,CAE1B,KAAK,iBAAiB,KAAK,EAAQ,CACnC,IAAM,EAAQ,KAAK,MAAM,GAEzB,OADA,KAAK,MAAM,GAAW,UACd,EAAR,CACE,IAAK,UACH,OAAO,KAAK,QAAQ,EAAO,CAC7B,IAAK,OACH,OAAO,KAAK,QAAQ,EAAS,EAAQ,EAAQ,CAC/C,QACE,OAAO,KAAK,YAAY,EAQ9B,OAAe,CACb,KAAK,QAAQ,OAAO,CAGtB,OAAe,CACb,KAAK,YAAY,CAGnB,SAAiB,CACf,KAAK,OAAO,CACZ,KAAK,OAAO,CAGd,YAAqB,OACnB,KAAK,oBACL,KAAK,UAAY,IACjB,EAAA,KAAK,cAAA,MAAA,EAAa,KAAK,CAGzB,YAAoC,CAClC,IAAM,EAAY,QAAQ,QAAQ,KAAK,CAEvC,OADA,KAAK,uBAAuB,CACrB,EAGT,QAAgB,EAA4B,EAA6B,EAAgC,CACvG,OAAO,IAAI,SAAe,EAAS,IAAW,CAC5C,IAAM,EAAQ,eAAiB,qBAAK,cAAA,KAAA,IAAA,GAAA,EAAa,KAAK,EAAE,EAAQ,CAC1D,MAAgB,CACpB,aAAa,EAAM,CACnB,KAAK,YAAc,IAAA,IAErB,KAAK,YAAc,CACjB,UACA,QAAW,CACT,GAAS,CACT,EAAQ,KAAK,EAEf,aAAgB,CACd,GAAS,CACT,EAAQ,KAAK,CACb,KAAK,uBAAuB,EAE9B,SAAY,CACV,GAAS,CACT,IAAM,EAAQ,KAAK,WAAW,EAAO,CACrC,KAAK,QAAQ,KAAK,kBAAmB,EAAM,CAC3C,EAAO,EAAM,EAEhB,EACD,CAGJ,QAAgB,EAA4C,CAC1D,IAAM,EAAQ,KAAK,WAAW,EAAO,CAErC,OADA,KAAK,QAAQ,KAAK,kBAAmB,EAAM,CACpC,QAAQ,OAAO,EAAM,CAG9B,WAAmB,EAA6B,CAC9C,OAAO,IAAI,EACT,0EAA0E,EAAO,kEACjF,IACD,CAGH,uBAAgC,CAC9B,IAAM,EAAa,KAAK,kBACnB,QAAQ,SAAS,CACnB,SAAW,GAAG,CACd,SAAW,CACN,IAAe,KAAK,oBAGxB,KAAK,UAAY,GACjB,KAAK,QAAQ,KAAK,YAAY,CAAC,GAC/B,CAGN,mBAA2B,EAAsB,CAC3C,KAAK,WACP,KAAK,QAAQ,EAAU,CAI3B,QAAgB,EAAsB,CACpC,QAAK,kBAAkB,KAAK,EAAU,CAClC,MAAK,WAGT,MAAK,WAAa,GAClB,GAAI,CACF,IAAI,EAAO,KAAK,kBAAkB,OAAO,CACzC,KAAO,GACL,KAAK,WAAW,EAAK,CACrB,EAAO,KAAK,kBAAkB,OAAO,QAE/B,CACR,KAAK,WAAa,KAItB,WAAmB,EAAsB,CACvC,GAAI,CACF,KAAK,QAAQ,KAAK,oBAAqB,EAAU,OAC1C,EAAO,CACd,KAAK,OAAO,MAAM,gFAAiF,EAAM,EAI7G,YAAgC,CAC9B,OAAA,EAAA,EAAA,EAAA,CAAY,EAAA,CAAA,EAAA,CAAA,CAAkB,KAAM,OAAQ,QAAS,OAAO,YAAY,KAAK,QAAQ,EAAE,CAGzF,MAAc,EAAoB,CAChC,KAAK,UAAU,EAAO,CAAC,SAAS,EAAa,IAAQ,KAAK,QAAQ,IAAI,EAAK,EAAY,CAAC,CAG1F,UAAkB,EAA8C,CAC9D,IAAM,EAAU,IAAI,IACpB,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAO,CAC/C,EAAQ,IAAI,EAAK,EAAgB,EAAK,EAAO,EAAE,KAAK,SAAS,CAAC,CAEhE,OAAO,IC/OX,MAmGM,EAAuC,CAC3C,cAAe,SAAY,GAC3B,oBAAuB,GACvB,MAAO,SAAY,GACpB,CAED,IAAM,EAAN,KAA+C,CAC7C,YACE,EACA,EACA,CAFgB,KAAA,OAAA,EACC,KAAA,WAAA,EAGnB,SAAgB,EAAa,EAAkB,CAC7C,KAAK,WAAW,SAAS,EAAK,EAAM,CAGtC,YAAmB,EAAa,CAC9B,KAAK,WAAW,YAAY,EAAI,CAGlC,cAAqB,EAAoB,CACvC,KAAK,WAAW,cAAc,EAAO,CAGvC,oBAA4B,CAC1B,KAAK,WAAW,KAAK,aAAa,CAGpC,wBAAgC,CAC9B,KAAK,WAAW,SAAS,aAAa,CAGxC,oBAA4B,CAC1B,KAAK,WAAW,KAAK,aAAa,CAGpC,mBAA2B,CACzB,KAAK,WAAW,KAAK,gBAAgB,CAGvC,uBAA+B,CAC7B,KAAK,WAAW,SAAS,gBAAgB,CAG3C,mBAA2B,CACzB,KAAK,WAAW,KAAK,gBAAgB,CAGvC,IAAW,gBAA0C,CACnD,OAAO,KAAK,WAAW,iBAI3B,MAAaC,GAAoB,EAAmC,IAA4C,SAC9G,IAAM,GAAA,EAAA,GAAA,KAAA,IAAA,GAAS,EAAS,SAAA,KAAU,GAAqB,CAA/B,EAClB,EAAa,IAAI,EAAmB,GAAA,EAAA,GAAA,KAAA,IAAA,GAAQ,EAAS,SAAA,KAAU,EAAE,CAAZ,EAAa,CAQxE,OAAO,IAAI,EAPI,IAAI,EACjB,EACA,cACA,EACA,CAAE,SAAQ,WAAY,CAAE,QAAA,GAAA,KAAA,IAAA,GAAS,EAAS,QAAS,CAAE,CACrD,CAAE,UAAW,EAAY,CAC1B,CACqC,EAAW,ECjJtC,EAAqB,GAAyC,CACzE,IAAM,EAAO,WACP,EAAW,EAAK,GAEtB,MADA,GAAK,GAA8B,EAAW,WACjC,CACX,EAAK,GAA8B,ICA1B,EAAoB,GACxB,EAAqB,CAAE,QAAS,aAAc,WAAY,QAAe,CAAE,EAAQ"}
1
+ {"version":3,"file":"testing.mjs","names":["encodeTestValue","encodeShared","createTestClient"],"sources":["../../../shared/src/testing/values.ts","../../../js-client-core/src/testing/values.ts","../../../js-client-core/src/testing/InMemoryConnection.ts","../../../js-client-core/src/testing/TestClient.ts","../../../js-client-core/src/testing/installTestClient.ts","../../src/client/testing.ts"],"sourcesContent":["import type { ConfigType } from \"../types\";\nimport { ConfigDirectorValidationError } from \"../errors\";\n\nexport type TestJsonValue = boolean | number | string | null | TestJsonValue[] | { [key: string]: TestJsonValue };\n\nexport type TestValue = boolean | number | string | TestJsonValue[] | { [key: string]: TestJsonValue };\n\nexport type TestValues = Record<string, TestValue>;\n\nexport type EncodedTestValue = { type: ConfigType; value: string };\n\nexport const encodeTestValue = (key: string, value: TestValue): EncodedTestValue => {\n if (key.trim().length == 0) {\n throw new ConfigDirectorValidationError(\"Invalid config key. The key of a test value cannot be empty.\");\n }\n return encode(key, value);\n};\n\nconst encode = (key: string, value: TestValue): EncodedTestValue => {\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 test 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): EncodedTestValue => {\n if (!Number.isFinite(value)) {\n throw new ConfigDirectorValidationError(`Invalid test 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 test 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 test 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 { ConfigState } from \"../types\";\nimport { encodeTestValue as encodeShared } from \"@shared/testing/values\";\nimport type { TestValue } from \"@shared/testing/values\";\n\nexport type { TestJsonValue, TestValue, TestValues } from \"@shared/testing/values\";\n\nexport const encodeTestValue = (key: string, value: TestValue, revision: number): ConfigState => {\n const encoded = encodeShared(key, value);\n return {\n id: `test-config:${key}`,\n key,\n type: encoded.type,\n value: encoded.value,\n valueId: `test-value:${key}:${revision}`,\n };\n};\n","import type {\n ClientConnectAction,\n ConfigDirectorContext,\n ConfigDirectorLogger,\n ConfigSet,\n ConfigState,\n Transport,\n TransportEvents,\n} from \"../types\";\nimport { Emitter } from \"../Emitter\";\nimport { ConfigDirectorConnectionError } from \"../errors\";\nimport { encodeTestValue, type TestValue, type TestValues } from \"./values\";\n\ntype ControlledAttempt = \"initialize\" | \"updateContext\";\n\ntype Armed = \"nothing\" | \"hold\" | \"failure\";\n\ntype PendingHold = {\n attempt: ControlledAttempt;\n end: () => void;\n complete: () => void;\n fail: () => void;\n};\n\nconst TEST_ENVIRONMENT = { environmentId: \"test-environment\", projectId: \"test-project\" };\n\nconst controlledAttemptFor = (reason: ClientConnectAction): ControlledAttempt | undefined => {\n switch (reason) {\n case \"initialization\":\n return \"initialize\";\n case \"context update\":\n return \"updateContext\";\n default:\n return undefined;\n }\n};\n\nexport class InMemoryConnection implements Transport {\n private readonly emitter = new Emitter<TransportEvents>();\n private readonly configs = new Map<string, ConfigState>();\n private readonly recordedContexts: ConfigDirectorContext[] = [];\n private readonly pendingDeliveries: ConfigSet[] = [];\n private readonly armed: Record<ControlledAttempt, Armed> = { initialize: \"nothing\", updateContext: \"nothing\" };\n private pendingHold: PendingHold | undefined;\n private connected = false;\n private delivering = false;\n private revision = 0;\n private attemptGeneration = 0;\n\n constructor(\n private readonly logger: ConfigDirectorLogger,\n values: TestValues,\n ) {\n this.store(values);\n }\n\n public get contextUpdates(): ConfigDirectorContext[] {\n return [...this.recordedContexts];\n }\n\n public setValue(key: string, value: TestValue) {\n const configState = encodeTestValue(key, value, ++this.revision);\n this.configs.set(key, configState);\n this.deliverIfConnected({ ...TEST_ENVIRONMENT, kind: \"delta\", configs: { [key]: configState } });\n }\n\n public removeValue(key: string) {\n this.configs.delete(key);\n this.deliverIfConnected(this.fullUpdate());\n }\n\n public replaceValues(values: TestValues) {\n const encoded = this.encodeAll(values);\n this.configs.clear();\n encoded.forEach((configState, key) => this.configs.set(key, configState));\n this.armed.initialize = \"nothing\";\n this.armed.updateContext = \"nothing\";\n this.deliverIfConnected(this.fullUpdate());\n }\n\n public hold(attempt: ControlledAttempt) {\n this.armed[attempt] = \"hold\";\n }\n\n public complete(attempt: ControlledAttempt) {\n if (this.pendingHold?.attempt === attempt) {\n this.pendingHold.complete();\n } else if (this.armed[attempt] === \"hold\") {\n this.armed[attempt] = \"nothing\";\n }\n }\n\n public fail(attempt: ControlledAttempt) {\n if (this.pendingHold?.attempt === attempt) {\n this.pendingHold.fail();\n } else {\n this.armed[attempt] = \"failure\";\n }\n }\n\n public connect(context: ConfigDirectorContext, timeout: number, reason: ClientConnectAction): Promise<this> {\n this.endAttempt();\n const attempt = controlledAttemptFor(reason);\n if (!attempt) {\n return this.connectNow();\n }\n this.recordedContexts.push(context);\n const armed = this.armed[attempt];\n this.armed[attempt] = \"nothing\";\n switch (armed) {\n case \"failure\":\n return this.failNow(reason);\n case \"hold\":\n return this.holdNow(attempt, reason, timeout);\n default:\n return this.connectNow();\n }\n }\n\n public on: Transport[\"on\"] = (name, handler) => this.emitter.on(name, handler);\n\n public off: Transport[\"off\"] = (name, handler) => this.emitter.off(name, handler);\n\n public clear() {\n this.emitter.clear();\n }\n\n public close() {\n this.endAttempt();\n }\n\n public dispose() {\n this.close();\n this.clear();\n }\n\n private endAttempt() {\n this.attemptGeneration++;\n this.connected = false;\n this.pendingHold?.end();\n }\n\n private connectNow(): Promise<this> {\n const connected = Promise.resolve(this);\n this.scheduleFirstDelivery();\n return connected;\n }\n\n private holdNow(attempt: ControlledAttempt, reason: ClientConnectAction, timeout: number): Promise<this> {\n return new Promise<this>((resolve, reject) => {\n const timer = setTimeout(() => this.pendingHold?.end(), timeout);\n const release = () => {\n clearTimeout(timer);\n this.pendingHold = undefined;\n };\n this.pendingHold = {\n attempt,\n end: () => {\n release();\n resolve(this);\n },\n complete: () => {\n release();\n resolve(this);\n this.scheduleFirstDelivery();\n },\n fail: () => {\n release();\n const error = this.fatalError(reason);\n this.emitter.emit(\"connectionError\", error);\n reject(error);\n },\n };\n });\n }\n\n private failNow(reason: ClientConnectAction): Promise<this> {\n const error = this.fatalError(reason);\n this.emitter.emit(\"connectionError\", error);\n return Promise.reject(error);\n }\n\n private fatalError(reason: ClientConnectAction) {\n return new ConfigDirectorConnectionError(\n `Connection failed with status: 401. Error: the test client failed this ${reason}. This is an unrecoverable error, will not attempt to reconnect.`,\n 401,\n );\n }\n\n private scheduleFirstDelivery() {\n const generation = this.attemptGeneration;\n void Promise.resolve()\n .then(() => {})\n .then(() => {\n if (generation !== this.attemptGeneration) {\n return;\n }\n this.connected = true;\n this.deliver(this.fullUpdate());\n });\n }\n\n private deliverIfConnected(configSet: ConfigSet) {\n if (this.connected) {\n this.deliver(configSet);\n }\n }\n\n private deliver(configSet: ConfigSet) {\n this.pendingDeliveries.push(configSet);\n if (this.delivering) {\n return;\n }\n this.delivering = true;\n try {\n let next = this.pendingDeliveries.shift();\n while (next) {\n this.emitCaught(next);\n next = this.pendingDeliveries.shift();\n }\n } finally {\n this.delivering = false;\n }\n }\n\n private emitCaught(configSet: ConfigSet) {\n try {\n this.emitter.emit(\"configSetReceived\", configSet);\n } catch (error) {\n this.logger.error(\"[InMemoryConnection] Error dispatching a config update from the test client: \", error);\n }\n }\n\n private fullUpdate(): ConfigSet {\n return { ...TEST_ENVIRONMENT, kind: \"full\", configs: Object.fromEntries(this.configs) };\n }\n\n private store(values: TestValues) {\n this.encodeAll(values).forEach((configState, key) => this.configs.set(key, configState));\n }\n\n private encodeAll(values: TestValues): Map<string, ConfigState> {\n const encoded = new Map<string, ConfigState>();\n for (const [key, value] of Object.entries(values)) {\n encoded.set(key, encodeTestValue(key, value, ++this.revision));\n }\n return encoded;\n }\n}\n","import { DefaultConfigDirectorClient } from \"../DefaultConfigDirectorClient\";\nimport { createDefaultLogger } from \"../logger\";\nimport type { ConfigDirectorClient, ConfigDirectorContext, ConfigDirectorLogger, IdentifyingSdkOptions } from \"../types\";\nimport type { TelemetryClient } from \"../telemetry\";\nimport { InMemoryConnection } from \"./InMemoryConnection\";\nimport type { TestValue, TestValues } from \"./values\";\n\nconst TEST_CLIENT_SDK_KEY = \"test-client\";\n\n/**\n * Options for {@link createTestClient}. Every option is optional.\n */\nexport type TestClientOptions = {\n /**\n * The config values the test client serves once its client connects, keyed by config key. The\n * config type follows from each value: a boolean, an integral number (`integer`), any other\n * number (`float`), a string, or a plain object or array (`json`).\n */\n values?: TestValues;\n /**\n * The connection timeout of the SDK client, in milliseconds. It bounds how long a held\n * `initialize` or `updateContext` waits. Defaults to the SDK's production timeout.\n */\n timeout?: number;\n /**\n * The logger the SDK client uses. Defaults to the SDK's console logger at `warn` level.\n */\n logger?: ConfigDirectorLogger;\n};\n\n/**\n * A real ConfigDirector client connected to an in-memory server that the test controls.\n *\n * The client under {@link TestClient.client} is the SDK's production client: value parsing, watchers,\n * events, and readiness behave as they do against ConfigDirector. Only the network connection and\n * telemetry are replaced, so no request is ever made and nothing is left running after the client is\n * disposed.\n */\nexport interface TestClient {\n /**\n * The SDK client to hand to the code under test. It starts uninitialized, like a production\n * client; `initialize` completes at once with the seeded values unless initialization is held.\n */\n readonly client: ConfigDirectorClient;\n\n /**\n * Stores `value` under `key` and, when the client is connected, delivers it as an update, so\n * reads, watchers, and `configsUpdated` handlers see it.\n */\n setValue(key: string, value: TestValue): void;\n\n /**\n * Removes `key` and, when the client is connected, delivers a full update without it, so reads\n * fall back to the in-code default value and watchers of `key` receive that default.\n */\n removeValue(key: string): void;\n\n /**\n * Replaces every stored value with `values`, disarms any armed hold or failure, and, when the\n * client is connected, delivers the new values as a full update.\n */\n replaceValues(values: TestValues): void;\n\n /**\n * Makes the next `initialize` wait until {@link completeInitialization} or\n * {@link failInitialization} is called, or until the client's timeout elapses.\n */\n holdInitialization(): void;\n\n /**\n * Delivers the stored values to a held `initialize`, so it completes with the client ready. When\n * a hold is armed but no `initialize` has started, it disarms the hold instead.\n */\n completeInitialization(): void;\n\n /**\n * Fails a held `initialize` with an unrecoverable connection error, or arms the next `initialize`\n * to fail. `initialize` completes promptly with the client not ready and `connectionError` fires.\n */\n failInitialization(): void;\n\n /**\n * Makes the next `updateContext` wait until {@link completeContextUpdate} or\n * {@link failContextUpdate} is called, or until the client's timeout elapses.\n */\n holdContextUpdate(): void;\n\n /**\n * Delivers the stored values to a held `updateContext`, or disarms an armed hold that no\n * `updateContext` has picked up.\n */\n completeContextUpdate(): void;\n\n /**\n * Fails a held `updateContext` with an unrecoverable connection error, or arms the next\n * `updateContext` to fail.\n */\n failContextUpdate(): void;\n\n /**\n * The context of every `initialize` and `updateContext` call, in call order. `initialize` without\n * a context records an empty context, and so does a call whose context has an invalid kind, since\n * the client connects without it. Resumes are not recorded.\n */\n readonly contextUpdates: ConfigDirectorContext[];\n}\n\nconst noopTelemetryClient: TelemetryClient = {\n updateContext: async () => {},\n evaluatedConfig: () => {},\n recordInvalidContext: () => {},\n close: async () => {},\n};\n\nclass InMemoryTestClient implements TestClient {\n constructor(\n public readonly client: ConfigDirectorClient,\n private readonly connection: InMemoryConnection,\n ) {}\n\n public setValue(key: string, value: TestValue) {\n this.connection.setValue(key, value);\n }\n\n public removeValue(key: string) {\n this.connection.removeValue(key);\n }\n\n public replaceValues(values: TestValues) {\n this.connection.replaceValues(values);\n }\n\n public holdInitialization() {\n this.connection.hold(\"initialize\");\n }\n\n public completeInitialization() {\n this.connection.complete(\"initialize\");\n }\n\n public failInitialization() {\n this.connection.fail(\"initialize\");\n }\n\n public holdContextUpdate() {\n this.connection.hold(\"updateContext\");\n }\n\n public completeContextUpdate() {\n this.connection.complete(\"updateContext\");\n }\n\n public failContextUpdate() {\n this.connection.fail(\"updateContext\");\n }\n\n public get contextUpdates(): ConfigDirectorContext[] {\n return this.connection.contextUpdates;\n }\n}\n\nexport const createTestClient = (sdkOptions: IdentifyingSdkOptions, options?: TestClientOptions): TestClient => {\n const logger = options?.logger ?? createDefaultLogger();\n const connection = new InMemoryConnection(logger, options?.values ?? {});\n const client = new DefaultConfigDirectorClient(\n noopTelemetryClient,\n TEST_CLIENT_SDK_KEY,\n sdkOptions,\n { logger, connection: { timeout: options?.timeout } },\n { transport: connection },\n );\n return new InMemoryTestClient(client, connection);\n};\n","import { INSTALLED_TEST_CLIENT_SLOT, type InstalledTestClientSlot } from \"../installedTestClient\";\nimport type { TestClient } from \"./TestClient\";\n\n/**\n * Makes every `ConfigDirectorProvider` mounted without a `client` prop, and every client\n * `withProvider` would build, use `testClient.client` instead of building a client, until the\n * returned function uninstalls it. Construction props such as `sdkKey` are ignored while installed;\n * `context` and `hooks` apply to the installed client as they do for a `client` prop. A provider\n * reads the installed client when it is constructed, so install before rendering.\n *\n * Installing again replaces the installed test client. Each uninstall function restores the test\n * client that was installed when it was created, so nested installs are uninstalled in reverse order.\n *\n * @param testClient The test client whose `client` providers should use\n * @returns A function that uninstalls it\n *\n * @example\n * let uninstallTestClient: () => void;\n * beforeEach(() => {\n * testClient = createTestClient({ values: { \"new-checkout\": true } });\n * uninstallTestClient = installTestClient(testClient);\n * });\n * afterEach(() => uninstallTestClient());\n */\nexport const installTestClient = (testClient: TestClient): (() => void) => {\n const slot = globalThis as InstalledTestClientSlot;\n const previous = slot[INSTALLED_TEST_CLIENT_SLOT];\n slot[INSTALLED_TEST_CLIENT_SLOT] = testClient.client;\n return () => {\n slot[INSTALLED_TEST_CLIENT_SLOT] = previous;\n };\n};\n","\"use client\";\n\nimport { createTestClient as createCoreTestClient } from \"@js-client-core/testing/index\";\nimport type { TestClient, TestClientOptions } from \"@js-client-core/testing/index\";\n\nexport { installTestClient } from \"@js-client-core/testing/index\";\nexport type { TestClient, TestClientOptions, TestValue, TestValues, TestJsonValue } from \"@js-client-core/testing/index\";\nexport { ConfigDirectorConnectionError, ConfigDirectorValidationError } from \"@shared/errors\";\n\n/**\n * Creates a {@link TestClient}: a real browser `ConfigDirectorClient` connected to an in-memory\n * server that the test controls. No network connection is opened and no telemetry is sent. Render\n * the client `ConfigDirectorProvider` with `client={testClient.client}`, or install it with\n * `installTestClient` for components that render their own provider.\n *\n * @param options {@link TestClientOptions}: the initial values, the client's timeout, and its logger\n * @returns A {@link TestClient} whose `client` is passed to the code under test\n *\n * @example\n * import { createTestClient } from \"@configdirector/nextjs-sdk/client/testing\";\n *\n * const testClient = createTestClient({ values: { \"new-checkout\": true } });\n * render(\n * <ConfigDirectorProvider client={testClient.client}>\n * <Checkout />\n * </ConfigDirectorProvider>,\n * );\n * act(() => testClient.setValue(\"new-checkout\", false));\n */\nexport const createTestClient = (options?: TestClientOptions): TestClient => {\n return createCoreTestClient({ sdkName: \"nextjs-sdk\", sdkVersion: \"__VERSION__\" }, options);\n};\n"],"mappings":"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"}
@@ -40,27 +40,40 @@ type ConfigEvaluation = {
40
40
  context?: ConfigDirectorContext;
41
41
  };
42
42
  /**
43
- * The user's context to be sent to ConfigDirector. This context will be used for targeting
44
- * rules evaluation.
43
+ * The context to be sent to ConfigDirector: the user or other entity, such as a device or an
44
+ * organization, that configs are evaluated for. This context will be used for targeting rules
45
+ * evaluation.
45
46
  */
46
47
  type ConfigDirectorContext = {
47
48
  /**
48
- * The user's identifier. This should be a value that uniquely identifies an application
49
- * user.
49
+ * The context's identifier. This should be a value that uniquely identifies the user or other
50
+ * entity among contexts of the same {@link kind}.
50
51
  * In the case of anonymous users, you could generate a UUID or alternatively not provide
51
52
  * the {@link id} and the SDK will generate a random UUID. However, keep in mind that this
52
- * value is used for segmenting users in percentage rollouts, and changes to the {@link id}
53
- * could result in the user being assigned to a different percentile.
53
+ * value is used for segmenting contexts in percentage rollouts, and changes to the {@link id}
54
+ * could result in the context being assigned to a different percentile.
54
55
  */
55
56
  id?: string;
56
57
  /**
57
- * The user's display name. This will be shown in the ConfigDirector dashboard and may be
58
+ * The kind of entity this context represents, such as `user`, `device`, `organization`,
59
+ * `tenant` or `listing`. You can use any other kind that fits your application. A kind has 1
60
+ * to 64 characters, using only lowercase letters (`a`-`z`), digits, `-` and `_`. It is used
61
+ * exactly as given, without trimming or changing its case.
62
+ *
63
+ * A context with an invalid kind is ignored as a whole: the SDK logs an error and evaluates
64
+ * as if no context had been provided.
65
+ *
66
+ * @default "user"
67
+ */
68
+ kind?: string;
69
+ /**
70
+ * The context's display name. This will be shown in the ConfigDirector dashboard and may be
58
71
  * used for targeting rules.
59
72
  */
60
73
  name?: string;
61
74
  /**
62
- * Any arbitrary traits for the current user. They will be shown in the ConfigDirector
63
- * dashboard and may be used for targeting rules.
75
+ * Any arbitrary traits for the context. They will be shown in the ConfigDirector dashboard
76
+ * and may be used for targeting rules.
64
77
  */
65
78
  traits?: {
66
79
  [key: string]: unknown;
@@ -126,17 +139,25 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
126
139
  * not attempt to re-connect and an error will be logged to the console or the provided
127
140
  * logger.
128
141
  *
129
- * @param context The current user's context to be used for evaluating targeting rules (optional).
142
+ * A context whose {@link ConfigDirectorContext.kind} is invalid is ignored as a whole: an error is
143
+ * logged, and the client connects as if no context had been provided.
144
+ *
145
+ * @param context The current context to be used for evaluating targeting rules (optional).
130
146
  */
131
147
  initialize(context?: ConfigDirectorContext): Promise<void>;
132
148
  /**
133
- * Updates the user's context and re-evaluates all config and flag values based on the new context.
149
+ * Updates the context and re-evaluates all config and flag values based on the new context.
150
+ *
151
+ * A context whose {@link ConfigDirectorContext.kind} is invalid is ignored as a whole: an error is
152
+ * logged, the previous context is no longer used, and the client connects as if no context had been
153
+ * provided.
134
154
  *
135
- * @param context The current user's context to be used for evaluating targeting rules (required).
155
+ * @param context The current context to be used for evaluating targeting rules (required).
136
156
  */
137
157
  updateContext(context: ConfigDirectorContext): Promise<void>;
138
158
  /**
139
- * Returns the current context the client is configured with, or undefined if there is no context.
159
+ * Returns the current context the client is configured with, or undefined if there is no context,
160
+ * including after a context with an invalid kind was ignored.
140
161
  *
141
162
  * Note that the value returned does not updated immediately after calling {@link updateContext}. Only
142
163
  * once the underlying connection was successful or timed out, the current context is updated. This is
@@ -217,4 +238,4 @@ interface ConfigDirectorClient extends EventProvider<ClientEvents> {
217
238
  }
218
239
  //#endregion
219
240
  export { ConfigDirectorLoggingLevel as a, ConnectionMode as c, ConfigDirectorLogger as i, ConfigDirectorClient as n, ConfigState as o, ConfigDirectorContext as r, ConfigValueType as s, ClientHooks as t };
220
- //# sourceMappingURL=types-fL7X5zwg.d.cts.map
241
+ //# sourceMappingURL=types-Cz68hQ6f.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-Cz68hQ6f.d.cts","names":[],"sources":["../../../js-client-core/src/Emitter.ts","../../../shared/src/types.ts","../../../js-client-core/src/types.ts"],"mappings":"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"}