{"version":3,"file":"index.cjs","names":["Hookified"],"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":["/**\n * Custom attributes that can contain any key-value pairs.\n */\nexport type CustomAttributes = Record<string, unknown>;\n\n/**\n * User information for evaluation context.\n */\nexport type ToggleUser = {\n\t/**\n\t * Unique identifier for the user.\n\t */\n\tid: string;\n\n\t/**\n\t * User's email address.\n\t */\n\temail?: string;\n\n\t/**\n\t * User's display name.\n\t */\n\tname?: string;\n\n\t/**\n\t * Custom attributes specific to the user.\n\t */\n\tcustomAttributes?: CustomAttributes;\n};\n\n/**\n * Evaluation context for Hyphen feature toggle evaluation.\n *\n * @example\n * ```typescript\n * const context: ToggleContext = {\n *   targetingKey: \"user-123\",\n *   ipAddress: \"203.0.113.42\",\n *   customAttributes: {\n *     subscriptionLevel: \"premium\",\n *     region: \"us-east\",\n *   },\n *   user: {\n *     id: \"user-123\",\n *     email: \"john.doe@example.com\",\n *     name: \"John Doe\",\n *     customAttributes: {\n *       role: \"admin\",\n *     },\n *   },\n * };\n * ```\n */\nexport type ToggleContext = {\n\t/**\n\t * Primary key used for targeting and bucketing.\n\t */\n\ttargetingKey?: string;\n\n\t/**\n\t * IP address of the user making the request.\n\t */\n\tipAddress?: string;\n\n\t/**\n\t * Custom attributes for evaluation context.\n\t */\n\tcustomAttributes?: CustomAttributes;\n\n\t/**\n\t * User information for evaluation.\n\t */\n\tuser?: ToggleUser;\n};\n\nexport type ToggleEvaluation = {\n\tapplication: string;\n\tenvironment: string;\n} & ToggleContext;\n\nexport interface Evaluation {\n\tkey: string;\n\tvalue: boolean | string | number | Record<string, unknown>;\n\ttype: \"boolean\" | \"string\" | \"number\" | \"object\";\n\treason?: unknown;\n\terrorMessage?: string;\n}\n\nexport interface EvaluationResponse {\n\ttoggles: Record<string, Evaluation>;\n}\n\nexport type GetOptions = {\n\tcontext?: ToggleContext;\n\tcache?: boolean;\n};\n\nexport enum ToggleEvents {\n\tError = \"error\",\n}\n","import { Hookified } from \"hookified\";\nimport {\n\ttype EvaluationResponse,\n\ttype GetOptions,\n\ttype ToggleContext,\n\ttype ToggleEvaluation,\n\tToggleEvents,\n} from \"./types.js\";\n\nexport type { ToggleContext } from \"./types.js\";\nexport { ToggleEvents } from \"./types.js\";\n\nexport type ToggleOptions = {\n\t/**\n\t * public api key\n\t */\n\tpublicApiKey?: string;\n\n\t/**\n\t * The default context to use when once is not passed\n\t */\n\tdefaultContext?: ToggleContext;\n\n\t/**\n\t * Horizon Endpoint Urls to use for Toggle. This will place these urls to be\n\t * load balanced. If endpoints fail it will attempt to use the default horizon\n\t * endpoint service.\n\t * @see {@link https://hyphen.ai/horizon} for more information\n\t */\n\thorizonUrls?: string[];\n\n\tapplicationId?: string;\n\n\tenvironment?: string;\n\n\tdefaultTargetKey?: string;\n};\n\nexport class Toggle extends Hookified {\n\tprivate _publicApiKey?: string;\n\tprivate _organizationId: string | undefined;\n\tprivate _applicationId: string | undefined;\n\tprivate _environment: string | undefined;\n\tprivate _horizonUrls: string[] = [];\n\n\tprivate _defaultContext: ToggleContext | undefined;\n\tprivate _defaultTargetingKey: string =\n\t\t`${Math.random().toString(36).substring(7)}`;\n\n\tconstructor(options?: ToggleOptions) {\n\t\t// hookified v2 defaults `throwOnEmptyListeners` to `true`, which would\n\t\t// cause `emit(ToggleEvents.Error, ...)` to re-throw when no listener is\n\t\t// attached. The SDK swallows evaluation errors and returns the caller's\n\t\t// default value, so we opt back into the v1 behavior.\n\t\tsuper({ throwOnEmptyListeners: false });\n\n\t\tif (options?.applicationId) {\n\t\t\tthis._applicationId = options.applicationId;\n\t\t}\n\n\t\tif (options?.environment) {\n\t\t\tthis._environment = options.environment;\n\t\t} else {\n\t\t\tthis._environment = \"development\";\n\t\t}\n\n\t\tif (options?.defaultContext) {\n\t\t\tthis._defaultContext = options.defaultContext;\n\t\t}\n\n\t\tif (options?.publicApiKey) {\n\t\t\tthis._publicApiKey = options.publicApiKey;\n\t\t\tthis._organizationId = this.getOrgIdFromPublicKey(this._publicApiKey);\n\t\t}\n\n\t\tif (options?.horizonUrls) {\n\t\t\tthis._horizonUrls = options.horizonUrls;\n\t\t} else {\n\t\t\tthis._horizonUrls = this.getDefaultHorizonUrls(this._publicApiKey);\n\t\t}\n\n\t\tif (options?.defaultTargetKey) {\n\t\t\tthis._defaultTargetingKey = options?.defaultTargetKey;\n\t\t} else {\n\t\t\tif (this._defaultContext) {\n\t\t\t\tthis._defaultTargetingKey = this.getTargetingKey(this._defaultContext);\n\t\t\t} else {\n\t\t\t\tthis._defaultTargetingKey = this.generateTargetKey();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets the public API key used for authentication.\n\t *\n\t * @returns The current public API key or undefined if not set\n\t */\n\tpublic get publicApiKey(): string | undefined {\n\t\treturn this._publicApiKey;\n\t}\n\n\t/**\n\t * Sets the public API key used for authentication.\n\t *\n\t * @param value - The public API key string or undefined to clear\n\t * @throws {Error} If the key doesn't start with \"public_\"\n\t */\n\tpublic set publicApiKey(value: string | undefined) {\n\t\tthis.setPublicKey(value);\n\t}\n\n\t/**\n\t * Gets the default context used for toggle evaluations.\n\t *\n\t * @returns The current default ToggleContext\n\t */\n\tpublic get defaultContext(): ToggleContext | undefined {\n\t\treturn this._defaultContext;\n\t}\n\n\t/**\n\t * Sets the default context used for toggle evaluations.\n\t *\n\t * @param value - The ToggleContext to use as default\n\t */\n\tpublic set defaultContext(value: ToggleContext) {\n\t\tthis._defaultContext = value;\n\t}\n\n\t/**\n\t * Gets the organization ID extracted from the public API key.\n\t *\n\t * @returns The organization ID string or undefined if not available\n\t */\n\tpublic get organizationId(): string | undefined {\n\t\treturn this._organizationId;\n\t}\n\n\t/**\n\t * Gets the Horizon endpoint URLs used for load balancing.\n\t *\n\t * These URLs are used to distribute requests across multiple Horizon endpoints.\n\t * If endpoints fail, the system will attempt to use the default horizon endpoint service.\n\t *\n\t * @returns Array of Horizon endpoint URLs\n\t * @see {@link https://hyphen.ai/horizon} for more information\n\t */\n\tpublic get horizonUrls(): string[] {\n\t\treturn this._horizonUrls;\n\t}\n\n\t/**\n\t * Sets the Horizon endpoint URLs for load balancing.\n\t *\n\t * Configures multiple Horizon endpoints that will be used for load balancing.\n\t * When endpoints fail, the system will fall back to the default horizon endpoint service.\n\t *\n\t * @param value - Array of Horizon endpoint URLs or empty array to clear\n\t * @see {@link https://hyphen.ai/horizon} for more information\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle();\n\t * toggle.horizonUrls = [\n\t *   'https://org1.toggle.hyphen.cloud',\n\t *   'https://org2.toggle.hyphen.cloud'\n\t * ];\n\t * ```\n\t */\n\tpublic set horizonUrls(value: string[]) {\n\t\tthis._horizonUrls = value;\n\t}\n\n\t/**\n\t * Gets the application ID used for toggle context.\n\t *\n\t * @returns The current application ID or undefined if not set\n\t */\n\tpublic get applicationId(): string | undefined {\n\t\treturn this._applicationId;\n\t}\n\n\t/**\n\t * Sets the application ID used for toggle context.\n\t *\n\t * @param value - The application ID string or undefined to clear\n\t */\n\tpublic set applicationId(value: string | undefined) {\n\t\tthis._applicationId = value;\n\t}\n\n\t/**\n\t * Gets the environment used for toggle context.\n\t *\n\t * @returns The current environment (defaults to 'development')\n\t */\n\tpublic get environment(): string | undefined {\n\t\treturn this._environment;\n\t}\n\n\t/**\n\t * Sets the environment used for toggle context.\n\t *\n\t * @param value - The environment string or undefined to clear\n\t */\n\tpublic set environment(value: string | undefined) {\n\t\tthis._environment = value;\n\t}\n\n\t/**\n\t * Gets the default targeting key used for toggle evaluations.\n\t *\n\t * @returns The current default targeting key or undefined if not set\n\t */\n\tpublic get defaultTargetingKey(): string {\n\t\treturn this._defaultTargetingKey;\n\t}\n\n\t/**\n\t * Sets the default targeting key used for toggle evaluations.\n\t *\n\t * @param value - The targeting key string or undefined to clear\n\t */\n\tpublic set defaultTargetingKey(value: string) {\n\t\tthis._defaultTargetingKey = value;\n\t}\n\n\tpublic async get<T>(\n\t\ttoggleKey: string,\n\t\tdefaultValue: T,\n\t\toptions?: GetOptions,\n\t): Promise<T> {\n\t\ttry {\n\t\t\tconst context: ToggleEvaluation = {\n\t\t\t\tapplication: this._applicationId ?? \"\",\n\t\t\t\tenvironment: this._environment ?? \"development\",\n\t\t\t};\n\n\t\t\tif (options?.context) {\n\t\t\t\tcontext.targetingKey = options?.context.targetingKey;\n\t\t\t\tcontext.ipAddress = options?.context.ipAddress;\n\t\t\t\tcontext.user = options?.context.user;\n\t\t\t\tcontext.customAttributes = options?.context.customAttributes;\n\t\t\t} else {\n\t\t\t\tcontext.targetingKey = this._defaultContext?.targetingKey;\n\t\t\t\tcontext.ipAddress = this._defaultContext?.ipAddress;\n\t\t\t\tcontext.user = this._defaultContext?.user;\n\t\t\t\tcontext.customAttributes = this._defaultContext?.customAttributes;\n\t\t\t}\n\n\t\t\tconst fetchOptions = { headers: {} as Record<string, string> };\n\n\t\t\tif (!context.targetingKey) {\n\t\t\t\tcontext.targetingKey = this.getTargetingKey(context);\n\t\t\t}\n\n\t\t\t// api key\n\t\t\tif (this._publicApiKey) {\n\t\t\t\tfetchOptions.headers[\"x-api-key\"] = this._publicApiKey;\n\t\t\t} else {\n\t\t\t\tthrow new Error(\"You must set the publicApiKey\");\n\t\t\t}\n\n\t\t\t// application\n\t\t\tif (context.application === \"\") {\n\t\t\t\tthrow new Error(\"You must set the applicationId\");\n\t\t\t}\n\n\t\t\tconst result = await this.fetch<EvaluationResponse>(\n\t\t\t\t\"/toggle/evaluate\",\n\t\t\t\tcontext,\n\t\t\t\tfetchOptions,\n\t\t\t);\n\n\t\t\tif (result?.toggles) {\n\t\t\t\treturn result.toggles[toggleKey].value as T;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.emit(ToggleEvents.Error, error);\n\t\t}\n\n\t\treturn defaultValue;\n\t}\n\n\t/**\n\t * Retrieves a boolean toggle value.\n\t *\n\t * This is a convenience method that wraps the generic get() method with boolean type safety.\n\t *\n\t * @param toggleKey - The key of the toggle to retrieve\n\t * @param defaultValue - The boolean value to return if the toggle is not found or an error occurs\n\t * @param options - Optional configuration including context for toggle evaluation\n\t * @returns Promise resolving to the boolean toggle value or defaultValue\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle({ publicApiKey: 'public_key', applicationId: 'app-id' });\n\t * const isFeatureEnabled = await toggle.getBoolean('feature-flag', false);\n\t * console.log(isFeatureEnabled); // true or false\n\t * ```\n\t */\n\tpublic async getBoolean(\n\t\ttoggleKey: string,\n\t\tdefaultValue: boolean,\n\t\toptions?: GetOptions,\n\t): Promise<boolean> {\n\t\treturn this.get<boolean>(toggleKey, defaultValue, options);\n\t}\n\n\t/**\n\t * Retrieves a string toggle value.\n\t *\n\t * This is a convenience method that wraps the generic get() method with string type safety.\n\t *\n\t * @param toggleKey - The key of the toggle to retrieve\n\t * @param defaultValue - The string value to return if the toggle is not found or an error occurs\n\t * @param options - Optional configuration including context for toggle evaluation\n\t * @returns Promise resolving to the string toggle value or defaultValue\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle({ publicApiKey: 'public_key', applicationId: 'app-id' });\n\t * const message = await toggle.getString('welcome-message', 'Hello World');\n\t * console.log(message); // 'Welcome to our app!' or 'Hello World'\n\t * ```\n\t */\n\tpublic async getString(\n\t\ttoggleKey: string,\n\t\tdefaultValue: string,\n\t\toptions?: GetOptions,\n\t): Promise<string> {\n\t\treturn this.get<string>(toggleKey, defaultValue, options);\n\t}\n\n\t/**\n\t * Retrieves an object toggle value.\n\t *\n\t * This is a convenience method that wraps the generic get() method with object type safety.\n\t * Note that the toggle service may return JSON as a string, which should be parsed if needed.\n\t *\n\t * @template T - The expected object type\n\t * @param toggleKey - The key of the toggle to retrieve\n\t * @param defaultValue - The object value to return if the toggle is not found or an error occurs\n\t * @param options - Optional configuration including context for toggle evaluation\n\t * @returns Promise resolving to the object toggle value or defaultValue\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle({ publicApiKey: 'public_key', applicationId: 'app-id' });\n\t * const config = await toggle.getObject('app-config', { theme: 'light' });\n\t * console.log(config); // { theme: 'dark', features: ['a', 'b'] } or { theme: 'light' }\n\t * ```\n\t */\n\tpublic async getObject<T extends object>(\n\t\ttoggleKey: string,\n\t\tdefaultValue: T,\n\t\toptions?: GetOptions,\n\t): Promise<T> {\n\t\treturn this.get<T>(toggleKey, defaultValue, options);\n\t}\n\n\t/**\n\t * Retrieves a number toggle value.\n\t *\n\t * This is a convenience method that wraps the generic get() method with number type safety.\n\t *\n\t * @param toggleKey - The key of the toggle to retrieve\n\t * @param defaultValue - The number value to return if the toggle is not found or an error occurs\n\t * @param options - Optional configuration including context for toggle evaluation\n\t * @returns Promise resolving to the number toggle value or defaultValue\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle({ publicApiKey: 'public_key', applicationId: 'app-id' });\n\t * const maxRetries = await toggle.getNumber('max-retries', 3);\n\t * console.log(maxRetries); // 5 or 3\n\t * ```\n\t */\n\tpublic async getNumber(\n\t\ttoggleKey: string,\n\t\tdefaultValue: number,\n\t\toptions?: GetOptions,\n\t): Promise<number> {\n\t\treturn this.get<number>(toggleKey, defaultValue, options);\n\t}\n\n\t/**\n\t * Makes an HTTP POST request to the specified URL with automatic authentication.\n\t *\n\t * This method uses browser-compatible fetch and automatically includes the\n\t * public API key in the x-api-key header if available. It supports load\n\t * balancing across multiple horizon URLs with fallback behavior.\n\t *\n\t * @template T - The expected response type\n\t * @param path - The API path to request (e.g., '/api/toggles')\n\t * @param payload - The JSON payload to send in the request body\n\t * @param options - Optional fetch configuration\n\t * @returns Promise resolving to the parsed JSON response\n\t * @throws {Error} If no horizon URLs are configured or all requests fail\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle({\n\t *   publicApiKey: 'public_your-key-here',\n\t *   horizonUrls: ['https://api.hyphen.cloud']\n\t * });\n\t *\n\t * interface ToggleResponse {\n\t *   enabled: boolean;\n\t *   value: string;\n\t * }\n\t *\n\t * const result = await toggle.fetch<ToggleResponse>('/api/toggle/feature-flag', {\n\t *   context: { targetingKey: 'user-123' }\n\t * });\n\t * console.log(result.enabled); // true/false\n\t * ```\n\t */\n\tpublic async fetch<T>(\n\t\tpath: string,\n\t\tpayload?: unknown,\n\t\toptions?: RequestInit,\n\t): Promise<T> {\n\t\tif (this._horizonUrls.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No horizon URLs configured. Set horizonUrls or provide a valid publicApiKey.\",\n\t\t\t);\n\t\t}\n\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t};\n\n\t\tif (options?.headers) {\n\t\t\tif (options.headers instanceof Headers) {\n\t\t\t\toptions.headers.forEach((value, key) => {\n\t\t\t\t\theaders[key] = value;\n\t\t\t\t});\n\t\t\t} else if (Array.isArray(options.headers)) {\n\t\t\t\toptions.headers.forEach(([key, value]) => {\n\t\t\t\t\theaders[key] = value;\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tObject.assign(headers, options.headers);\n\t\t\t}\n\t\t}\n\n\t\tif (this._publicApiKey) {\n\t\t\theaders[\"x-api-key\"] = this._publicApiKey;\n\t\t}\n\n\t\tconst fetchOptions: RequestInit = {\n\t\t\tmethod: \"POST\",\n\t\t\t...options,\n\t\t\theaders,\n\t\t\tbody: payload ? JSON.stringify(payload) : options?.body,\n\t\t};\n\n\t\tconst errors: Error[] = [];\n\n\t\tfor (const baseUrl of this._horizonUrls) {\n\t\t\ttry {\n\t\t\t\tconst url = `${baseUrl.replace(/\\/$/, \"\")}${path.startsWith(\"/\") ? path : `/${path}`}`;\n\t\t\t\tconst response = await fetch(url, fetchOptions);\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`HTTP ${response.status}: ${response.statusText}`);\n\t\t\t\t}\n\n\t\t\t\tconst data = (await response.json()) as T;\n\t\t\t\treturn data;\n\t\t\t} catch (error) {\n\t\t\t\tconst fetchError =\n\t\t\t\t\terror instanceof Error ? error : new Error(\"Unknown fetch error\");\n\t\t\t\terrors.push(fetchError);\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`All horizon URLs failed. Last errors: ${errors.map((e) => e.message).join(\", \")}`,\n\t\t);\n\t}\n\n\t/**\n\t * Validates and sets the public API key. This is used internally\n\t *\n\t * @param key - The public API key string or undefined to clear\n\t * @throws {Error} If the key doesn't start with \"public_\"\n\t */\n\tpublic setPublicKey(key: string | undefined): void {\n\t\tif (key !== undefined && !key.startsWith(\"public_\")) {\n\t\t\tthrow new Error(\"Public API key must start with 'public_'\");\n\t\t}\n\t\tthis._publicApiKey = key;\n\t}\n\n\t/**\n\t * Extracts the organization ID from a public API key.\n\t *\n\t * The public key format is: `public_<base64-encoded-data>`\n\t * The base64 data contains: `orgId:secretData`\n\t * Only alphanumeric characters, underscores, and hyphens are considered valid in org IDs.\n\t *\n\t * @param publicKey - The public API key to extract the organization ID from\n\t * @returns The organization ID if valid and extractable, undefined otherwise\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle();\n\t * const orgId = toggle.getOrgIdFromPublicKey('public_dGVzdC1vcmc6c2VjcmV0');\n\t * console.log(orgId); // 'test-org'\n\t * ```\n\t */\n\tpublic getOrgIdFromPublicKey(publicKey: string): string | undefined {\n\t\ttry {\n\t\t\tconst keyWithoutPrefix = publicKey.replace(/^public_/, \"\");\n\t\t\t// Browsers / modern runtimes expose `atob`; fall back to Node's\n\t\t\t// `Buffer` without depending on @types/node globally.\n\t\t\tconst nodeBuffer = (\n\t\t\t\tglobalThis as typeof globalThis & {\n\t\t\t\t\tBuffer?: {\n\t\t\t\t\t\tfrom(input: string, encoding: string): { toString(): string };\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t).Buffer;\n\t\t\tconst decoded = globalThis.atob\n\t\t\t\t? globalThis.atob(keyWithoutPrefix)\n\t\t\t\t: (nodeBuffer?.from(keyWithoutPrefix, \"base64\").toString() ?? \"\");\n\t\t\tconst [orgId] = decoded.split(\":\");\n\t\t\tconst isValidOrgId = /^[a-zA-Z0-9_-]+$/.test(orgId);\n\t\t\treturn isValidOrgId ? orgId : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Builds the default Horizon API URL for the given public key.\n\t *\n\t * If a valid organization ID can be extracted from the public key, returns an\n\t * organization-specific URL. Otherwise, returns the default fallback URL.\n\t *\n\t * @param publicKey - The public API key to build the URL for\n\t * @returns Organization-specific URL or default fallback URL\n\t *\n\t * @example\n\t * ```typescript\n\t * const toggle = new Toggle();\n\t *\n\t * // With valid org ID\n\t * const orgUrl = toggle.buildDefaultHorizonUrl('public_dGVzdC1vcmc6c2VjcmV0');\n\t * console.log(orgUrl); // 'https://test-org.toggle.hyphen.cloud'\n\t *\n\t * // With invalid key\n\t * const defaultUrl = toggle.buildDefaultHorizonUrl('invalid-key');\n\t * console.log(defaultUrl); // 'https://toggle.hyphen.cloud'\n\t * ```\n\t */\n\tpublic getDefaultHorizonUrl(publicKey?: string): string {\n\t\tif (publicKey) {\n\t\t\tconst orgId = this.getOrgIdFromPublicKey(publicKey);\n\t\t\treturn orgId\n\t\t\t\t? `https://${orgId}.toggle.hyphen.cloud`\n\t\t\t\t: \"https://toggle.hyphen.cloud\";\n\t\t}\n\n\t\treturn \"https://toggle.hyphen.cloud\";\n\t}\n\n\t/**\n\t * Will get the urls. If you pass in the public key it will provide two urls.\n\t * @param publicKey\n\t * @returns\n\t */\n\tpublic getDefaultHorizonUrls(publicKey?: string): string[] {\n\t\tlet result = [this.getDefaultHorizonUrl()];\n\t\tif (publicKey) {\n\t\t\tconst defaultUrl = result[0];\n\t\t\tconst orgUrl = this.getDefaultHorizonUrl(publicKey);\n\t\t\tresult = [];\n\t\t\tif (orgUrl !== defaultUrl) {\n\t\t\t\tresult.push(orgUrl);\n\t\t\t}\n\t\t\t// make the default url the secondary\n\t\t\tresult.push(defaultUrl);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Generates a unique targeting key based on available context.\n\t *\n\t * @returns A targeting key in the format: `[app]-[env]-[random]` or simplified versions\n\t */\n\tpublic generateTargetKey(): string {\n\t\tconst randomSuffix = Math.random().toString(36).substring(7);\n\t\tconst app = this._applicationId || \"\";\n\t\tconst env = this._environment || \"\";\n\n\t\t// Build key components in order of preference\n\t\tconst components = [app, env, randomSuffix].filter(Boolean);\n\n\t\treturn components.join(\"-\");\n\t}\n\n\t/**\n\t * Extracts targeting key from a toggle context with fallback logic.\n\t *\n\t * @param context - The toggle context to extract targeting key from\n\t * @returns The targeting key string\n\t */\n\tprivate getTargetingKey(context: ToggleContext): string {\n\t\tif (context.targetingKey) {\n\t\t\treturn context.targetingKey;\n\t\t}\n\t\tif (context.user) {\n\t\t\treturn context.user.id;\n\t\t}\n\t\t// TODO: what is a better way to do this? Should we also have a service property so we don't add the random value?\n\t\treturn this._defaultTargetingKey;\n\t}\n}\n"],"mappings":"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"}