{"version":3,"file":"devtools.mjs","names":["GrowthBook","UserScopedGrowthBook","applyDevtoolsState","devtoolsState","gb","inDevMode","attributes","setAttributeOverrides","features","map","Map","Object","entries","setForcedFeatures","experiments","setForcedVariations","devtoolsPlugin","Error","devtoolsNextjsPlugin","searchParams","requestCookies","request","extractGbDebugPayload","_gbdebug","URLSearchParams","get","undefined","value","payload","nextUrl","cookies","state","JSON","parse","e","console","error","devtoolsExpressPlugin","query","getDebugScriptContents","source","event","getDebugEvent","stringify","apiHost","clientKey","getApiInfo","logs","sdkInfo","version","getDecryptedPayload","getAttributes","userContext","getUserContext","getVersion","attributeOverrides"],"sources":["../../../src/plugins/devtools.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { GrowthBook } from \"../GrowthBook\";\nimport {\n Attributes,\n FeatureApiResponse,\n LogUnion,\n Plugin,\n} from \"../types/growthbook\";\nimport { GrowthBookClient, UserScopedGrowthBook } from \"../GrowthBookClient\";\n\nexport type DevtoolsState = {\n attributes?: Record;\n features?: Record;\n experiments?: Record;\n};\n\nexport interface NextjsReadonlyRequestCookiesCompat {\n get: (name: string) => { name: string; value: string } | undefined;\n}\nexport interface NextjsRequestCompat {\n nextUrl: {\n searchParams: URLSearchParams;\n };\n cookies: {\n get: (name: string) => { name: string; value: string } | undefined;\n };\n}\nexport interface ExpressRequestCompat {\n cookies: Record;\n query: Record;\n [key: string]: unknown;\n}\n\nfunction applyDevtoolsState(\n devtoolsState: DevtoolsState,\n gb: GrowthBook | UserScopedGrowthBook,\n) {\n // Only enable in dev mode\n if (!gb.inDevMode()) {\n return;\n }\n\n if (\n devtoolsState.attributes &&\n typeof devtoolsState.attributes === \"object\"\n ) {\n gb.setAttributeOverrides(devtoolsState.attributes);\n }\n if (devtoolsState.features && typeof devtoolsState.features === \"object\") {\n const map = new Map(Object.entries(devtoolsState.features));\n gb.setForcedFeatures(map);\n }\n if (\n devtoolsState.experiments &&\n typeof devtoolsState.experiments === \"object\"\n ) {\n gb.setForcedVariations(devtoolsState.experiments);\n }\n}\n\nexport function devtoolsPlugin(devtoolsState?: DevtoolsState): Plugin {\n return (gb: GrowthBook | UserScopedGrowthBook | GrowthBookClient) => {\n // Only works for user-scoped GrowthBook instances\n if (\"createScopedInstance\" in gb) {\n throw new Error(\n \"devtoolsPlugin can only be set on a user-scoped instance\",\n );\n }\n if (devtoolsState) {\n applyDevtoolsState(devtoolsState, gb);\n }\n };\n}\n\n/**\n * For NextJS environments.\n * When using server components, use the `searchParams` and `requestCookies` fields.\n * - Note: In NextJS 15+, you should await these values before passing them to the plugin\n * When using middleware / api routes, provide the `request` field instead.\n */\nexport function devtoolsNextjsPlugin({\n searchParams,\n requestCookies,\n request,\n}: {\n searchParams?: { _gbdebug?: string };\n requestCookies?: NextjsReadonlyRequestCookiesCompat;\n request?: NextjsRequestCompat;\n}): Plugin {\n function extractGbDebugPayload({\n searchParams,\n requestCookies,\n }: {\n searchParams?: { _gbdebug?: string } | URLSearchParams;\n requestCookies?: NextjsReadonlyRequestCookiesCompat;\n }): string | undefined {\n if (searchParams) {\n if (\"_gbdebug\" in searchParams) {\n return searchParams._gbdebug;\n }\n if (searchParams instanceof URLSearchParams) {\n return searchParams.get(\"_gbdebug\") ?? undefined;\n }\n }\n return requestCookies?.get(\"_gbdebug\")?.value;\n }\n\n return (gb: GrowthBook | UserScopedGrowthBook | GrowthBookClient) => {\n let payload = extractGbDebugPayload({ searchParams, requestCookies });\n\n if (!payload && request) {\n payload = extractGbDebugPayload({\n searchParams: request.nextUrl.searchParams,\n requestCookies: request.cookies,\n });\n }\n\n let state: DevtoolsState = {};\n if (payload) {\n try {\n state = JSON.parse(payload);\n } catch (e) {\n console.error(\"cannot parse devtools payload\", e);\n }\n }\n\n devtoolsPlugin(state)(gb);\n };\n}\n\n/**\n * Intended to be used with cookieParser() middleware from npm: 'cookie-parser'.\n */\nexport function devtoolsExpressPlugin({\n request,\n}: {\n request?: ExpressRequestCompat;\n}): Plugin {\n return (gb: GrowthBook | UserScopedGrowthBook | GrowthBookClient) => {\n let payload =\n typeof request?.query?.[\"_gbdebug\"] === \"string\"\n ? request.query[\"_gbdebug\"]\n : undefined;\n if (!payload) {\n payload =\n typeof request?.cookies?.[\"_gbdebug\"] === \"string\"\n ? request.cookies[\"_gbdebug\"]\n : undefined;\n }\n\n let state: DevtoolsState = {};\n if (payload) {\n try {\n state = JSON.parse(payload);\n } catch (e) {\n console.error(\"cannot parse devtools payload\", e);\n }\n }\n\n devtoolsPlugin(state)(gb);\n };\n}\n\nexport type SdkInfo = {\n apiHost: string;\n clientKey: string;\n source?: string;\n version?: string;\n payload?: FeatureApiResponse;\n attributes?: Attributes;\n};\nexport type LogEvent = {\n logs: LogUnion[];\n sdkInfo?: SdkInfo;\n};\n/**\n * Helper method to get debug script contents for DevTools\n * @param gb - GrowthBook instance. DevMode must be enabled to view log events.\n * @param {string} [source] - Label these events for ease of reading in DevTools\n * @example\n * A React logger component (implement yourself):\n ```\n return (\n