@sanity/personalization-plugin 2.5.0 → 3.0.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.
- package/LICENSE +1 -1
- package/README.md +570 -144
- package/dist/growthbook/index.d.ts +12 -15
- package/dist/growthbook/index.d.ts.map +1 -0
- package/dist/growthbook/index.js +104 -68
- package/dist/growthbook/index.js.map +1 -1
- package/dist/index.d.ts +212 -252
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +398 -301
- package/dist/index.js.map +1 -1
- package/dist/launchDarkly/index.d.ts +9 -12
- package/dist/launchDarkly/index.d.ts.map +1 -0
- package/dist/launchDarkly/index.js +74 -46
- package/dist/launchDarkly/index.js.map +1 -1
- package/package.json +35 -77
- package/dist/growthbook/index.d.mts +0 -15
- package/dist/growthbook/index.mjs +0 -124
- package/dist/growthbook/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -267
- package/dist/index.mjs +0 -472
- package/dist/index.mjs.map +0 -1
- package/dist/launchDarkly/index.d.mts +0 -12
- package/dist/launchDarkly/index.mjs +0 -107
- package/dist/launchDarkly/index.mjs.map +0 -1
- package/sanity.json +0 -8
- package/src/components/Array.tsx +0 -68
- package/src/components/ExperimentContext.tsx +0 -65
- package/src/components/ExperimentField.tsx +0 -138
- package/src/components/ExperimentInput.tsx +0 -75
- package/src/components/ExperimentItem.tsx +0 -10
- package/src/components/Select.tsx +0 -43
- package/src/components/VariantInput.tsx +0 -19
- package/src/components/VariantPreview.tsx +0 -75
- package/src/fieldExperiments.tsx +0 -266
- package/src/growthbook/Components/GrowthbookContext.tsx +0 -38
- package/src/growthbook/Components/Secrets.tsx +0 -47
- package/src/growthbook/index.ts +0 -54
- package/src/growthbook/types.ts +0 -15
- package/src/growthbook/utils.ts +0 -94
- package/src/index.ts +0 -3
- package/src/launchDarkly/components/LaunchDarklyContext.tsx +0 -36
- package/src/launchDarkly/components/Secrets.tsx +0 -46
- package/src/launchDarkly/index.ts +0 -52
- package/src/launchDarkly/types.ts +0 -193
- package/src/launchDarkly/utils.ts +0 -54
- package/src/types.ts +0 -245
- package/src/utils/flattenSchemaType.ts +0 -47
- package/v2-incompatible.js +0 -11
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import {FieldDefinition} from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export {}
|
|
1
|
+
import { FieldDefinition } from "sanity";
|
|
2
|
+
type LaunchDarklyFieldLevelConfig = {
|
|
3
|
+
fields: (string | FieldDefinition)[];
|
|
4
|
+
projectKey: string;
|
|
5
|
+
tags?: string[];
|
|
6
|
+
};
|
|
7
|
+
declare const fieldLevelExperiments: import("sanity").Plugin<LaunchDarklyFieldLevelConfig>;
|
|
8
|
+
export { fieldLevelExperiments };
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/launchDarkly/types.ts","../../src/launchDarkly/index.ts"],"mappings":";KAEY,4BAAA;EACV,MAAA,YAAkB,eAAe;EACjC,UAAA;EACA,IAAA;AAAA;AAAA,cCGW,qBAAA,mBAAqB,MAAA,CAAA,4BAAA"}
|
|
@@ -1,44 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import { definePlugin, isObjectInputProps } from "sanity";
|
|
2
|
+
import { flattenSchemaType, fieldLevelExperiments as fieldLevelExperiments$1 } from "../index.js";
|
|
3
|
+
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
+
import { c } from "react/compiler-runtime";
|
|
5
|
+
import { useState, useEffect, createContext, useContext } from "react";
|
|
6
|
+
import { useSecrets, SettingsView } from "@sanity/studio-secrets";
|
|
7
|
+
const namespace = "launchdarkly", pluginConfigKeys = [{
|
|
8
|
+
key: "apiKey",
|
|
9
|
+
title: "Your secret API key"
|
|
10
|
+
}], Secrets = (props) => {
|
|
11
|
+
const $ = c(12), {
|
|
12
|
+
secrets,
|
|
13
|
+
loading
|
|
14
|
+
} = useSecrets(namespace), {
|
|
15
|
+
setSecret
|
|
16
|
+
} = useLaunchDarklyContext(), [showSettings, setShowSettings] = useState(!1);
|
|
17
|
+
let t0, t1;
|
|
18
|
+
if ($[0] !== loading || $[1] !== secrets || $[2] !== setSecret ? (t0 = () => {
|
|
12
19
|
if (!loading)
|
|
13
20
|
return !secrets && !loading ? (setSecret(void 0), setShowSettings(!0)) : (setSecret(secrets.apiKey), setShowSettings(!1));
|
|
14
|
-
}, [secrets, loading, setSecret]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
21
|
+
}, t1 = [secrets, loading, setSecret], $[0] = loading, $[1] = secrets, $[2] = setSecret, $[3] = t0, $[4] = t1) : (t0 = $[3], t1 = $[4]), useEffect(t0, t1), !showSettings) {
|
|
22
|
+
let t22;
|
|
23
|
+
return $[5] !== props ? (t22 = props.renderDefault(props), $[5] = props, $[6] = t22) : t22 = $[6], t22;
|
|
24
|
+
}
|
|
25
|
+
let t2;
|
|
26
|
+
$[7] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (t2 = /* @__PURE__ */ jsx(SettingsView, { title: `${namespace} api key`, namespace, keys: pluginConfigKeys, onClose: () => {
|
|
27
|
+
setShowSettings(!1);
|
|
28
|
+
} }), $[7] = t2) : t2 = $[7];
|
|
29
|
+
let t3;
|
|
30
|
+
$[8] !== props ? (t3 = props.renderDefault(props), $[8] = props, $[9] = t3) : t3 = $[9];
|
|
31
|
+
let t4;
|
|
32
|
+
return $[10] !== t3 ? (t4 = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
|
+
t2,
|
|
34
|
+
t3
|
|
35
|
+
] }), $[10] = t3, $[11] = t4) : t4 = $[11], t4;
|
|
36
|
+
}, LAUNCHDARKLY_CONFIG_DEFAULT = {}, LaunchDarklyContext = createContext({
|
|
29
37
|
setSecret: () => {
|
|
30
38
|
},
|
|
31
39
|
secret: void 0
|
|
32
40
|
});
|
|
33
41
|
function useLaunchDarklyContext() {
|
|
34
|
-
return
|
|
42
|
+
return useContext(LaunchDarklyContext);
|
|
35
43
|
}
|
|
36
44
|
function LaunchDarklyProvider(props) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const $ = c(8), {
|
|
46
|
+
launchDarklyFieldPluginConfig
|
|
47
|
+
} = props, [secret, setSecret] = useState();
|
|
48
|
+
let t0;
|
|
49
|
+
$[0] !== launchDarklyFieldPluginConfig || $[1] !== secret ? (t0 = {
|
|
50
|
+
...launchDarklyFieldPluginConfig,
|
|
51
|
+
secret,
|
|
52
|
+
setSecret
|
|
53
|
+
}, $[0] = launchDarklyFieldPluginConfig, $[1] = secret, $[2] = t0) : t0 = $[2];
|
|
54
|
+
const context = t0;
|
|
55
|
+
let t1;
|
|
56
|
+
$[3] !== props ? (t1 = /* @__PURE__ */ jsx(Secrets, { ...props }), $[3] = props, $[4] = t1) : t1 = $[4];
|
|
57
|
+
let t2;
|
|
58
|
+
return $[5] !== context || $[6] !== t1 ? (t2 = /* @__PURE__ */ jsx(LaunchDarklyContext.Provider, { value: context, children: t1 }), $[5] = context, $[6] = t1, $[7] = t2) : t2 = $[7], t2;
|
|
42
59
|
}
|
|
43
60
|
const getExperiments = async ({
|
|
44
61
|
client,
|
|
@@ -58,7 +75,9 @@ const getExperiments = async ({
|
|
|
58
75
|
headers: {
|
|
59
76
|
Authorization: secret
|
|
60
77
|
}
|
|
61
|
-
}), {
|
|
78
|
+
}), {
|
|
79
|
+
items
|
|
80
|
+
} = await responseFlags.json(), experiments = items.map((flag) => ({
|
|
62
81
|
id: flag.key,
|
|
63
82
|
label: flag.name,
|
|
64
83
|
variants: flag.variations.map((variation) => ({
|
|
@@ -69,23 +88,30 @@ const getExperiments = async ({
|
|
|
69
88
|
featureExperiments.push(...experiments), items.length !== limit && (hasMore = !1);
|
|
70
89
|
}
|
|
71
90
|
return featureExperiments;
|
|
72
|
-
}, fieldLevelExperiments =
|
|
73
|
-
const pluginConfig = {
|
|
91
|
+
}, fieldLevelExperiments = definePlugin((config) => {
|
|
92
|
+
const pluginConfig = {
|
|
93
|
+
...LAUNCHDARKLY_CONFIG_DEFAULT,
|
|
94
|
+
...config
|
|
95
|
+
}, {
|
|
96
|
+
fields,
|
|
97
|
+
projectKey,
|
|
98
|
+
tags
|
|
99
|
+
} = pluginConfig;
|
|
74
100
|
return {
|
|
75
101
|
name: "sanity-growthbook-personalistaion-plugin-field-level-experiments",
|
|
76
|
-
plugins: [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
plugins: [fieldLevelExperiments$1({
|
|
103
|
+
fields,
|
|
104
|
+
experiments: (client) => getExperiments({
|
|
105
|
+
client,
|
|
106
|
+
projectKey,
|
|
107
|
+
tags
|
|
108
|
+
}),
|
|
109
|
+
experimentNameOverride: "flag"
|
|
110
|
+
})],
|
|
83
111
|
form: {
|
|
84
112
|
components: {
|
|
85
113
|
input: (props) => {
|
|
86
|
-
if (!(props.id === "root" &&
|
|
87
|
-
(field) => field.type.name
|
|
88
|
-
).some((name) => name.startsWith("flag")))
|
|
114
|
+
if (!(props.id === "root" && isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map((field) => field.type.name).some((name) => name.startsWith("flag")))
|
|
89
115
|
return props.renderDefault(props);
|
|
90
116
|
const providerProps = {
|
|
91
117
|
...props,
|
|
@@ -99,5 +125,7 @@ const getExperiments = async ({
|
|
|
99
125
|
}
|
|
100
126
|
};
|
|
101
127
|
});
|
|
102
|
-
|
|
128
|
+
export {
|
|
129
|
+
fieldLevelExperiments
|
|
130
|
+
};
|
|
103
131
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/launchDarkly/components/Secrets.tsx","../../src/launchDarkly/components/LaunchDarklyContext.tsx","../../src/launchDarkly/utils.ts","../../src/launchDarkly/index.ts"],"sourcesContent":["import {SettingsView, useSecrets} from '@sanity/studio-secrets'\nimport {useEffect, useState} from 'react'\nimport {ObjectInputProps} from 'sanity'\n\nimport {useLaunchDarklyContext} from './LaunchDarklyContext'\n\nconst namespace = 'launchdarkly'\nconst pluginConfigKeys = [\n {\n key: 'apiKey',\n title: 'Your secret API key',\n },\n]\n\nexport const Secrets = (props: ObjectInputProps) => {\n const {secrets, loading} = useSecrets(namespace) as {secrets: {apiKey: string}; loading: boolean}\n const {setSecret} = useLaunchDarklyContext()\n const [showSettings, setShowSettings] = useState<boolean>(false)\n\n useEffect(() => {\n if (loading) return undefined\n if (!secrets && !loading) {\n setSecret(undefined)\n return setShowSettings(true)\n }\n setSecret(secrets.apiKey)\n return setShowSettings(false)\n }, [secrets, loading, setSecret])\n\n if (!showSettings) {\n return props.renderDefault(props)\n }\n return (\n <>\n <SettingsView\n title={`${namespace} api key`}\n namespace={namespace}\n keys={pluginConfigKeys}\n onClose={() => {\n setShowSettings(false)\n }}\n />\n {props.renderDefault(props)}\n </>\n )\n}\n","import {createContext, useContext, useMemo, useState} from 'react'\nimport {ObjectInputProps} from 'sanity'\n\nimport {LaunchDarklyContextProps, LaunchDarklyFieldLevelConfig} from '../types'\nimport {Secrets} from './Secrets'\n\nexport const LAUNCHDARKLY_CONFIG_DEFAULT = {}\n\nexport const LaunchDarklyContext = createContext<LaunchDarklyContextProps>({\n setSecret: () => undefined,\n secret: undefined,\n})\n\nexport function useLaunchDarklyContext() {\n return useContext(LaunchDarklyContext)\n}\n\ntype LaunchDarklyProps = ObjectInputProps & {\n launchDarklyFieldPluginConfig: LaunchDarklyFieldLevelConfig\n}\n\nexport function LaunchDarklyProvider(props: LaunchDarklyProps) {\n const {launchDarklyFieldPluginConfig} = props\n const [secret, setSecret] = useState<string | undefined>()\n\n const context = useMemo(\n () => ({...launchDarklyFieldPluginConfig, secret, setSecret}),\n [launchDarklyFieldPluginConfig, secret, setSecret],\n )\n\n return (\n <LaunchDarklyContext.Provider value={context}>\n <Secrets {...props} />\n </LaunchDarklyContext.Provider>\n )\n}\n","import {SanityClient} from 'sanity'\n\nimport {ExperimentType} from '../types'\nimport {LaunchDarklyFieldLevelConfig, LaunchDarklyFlagItem} from './types'\n\nexport const getExperiments = async ({\n client,\n projectKey,\n tags,\n}: Omit<LaunchDarklyFieldLevelConfig, 'fields'> & {client: SanityClient}): Promise<\n ExperimentType[]\n> => {\n const query = `*[_id == 'secrets.launchdarkly'][0].secrets.apiKey`\n\n const secret = await client.fetch(query) // secret is stored in the content lake using @sanity/studio-secrets\n if (!secret) return []\n\n const url = new URL(`https://app.launchdarkly.com/api/v2/flags/${projectKey}`)\n\n if (tags) {\n url.searchParams.set('filter', `tags:${tags.join('+')}`)\n }\n\n const featureExperiments: ExperimentType[] = []\n let hasMore = true\n const offset = 0\n const limit = 10\n\n while (hasMore) {\n url.searchParams.set('offset', offset.toString())\n url.searchParams.set('limit', limit.toString())\n const responseFlags = await fetch(url, {\n headers: {\n Authorization: secret,\n },\n })\n\n const {items} = await responseFlags.json()\n const experiments = items.map((flag: LaunchDarklyFlagItem) => ({\n id: flag.key,\n label: flag.name,\n variants: flag.variations.map((variation) => ({\n id: variation.value.toString(),\n label: variation.name ?? variation.value.toString(),\n })),\n }))\n featureExperiments.push(...experiments)\n if (items.length !== limit) {\n hasMore = false\n }\n }\n\n return featureExperiments\n}\n","import {definePlugin, isObjectInputProps} from 'sanity'\n\nimport {fieldLevelExperiments as baseFieldLevelExperiments} from '../fieldExperiments'\nimport {flattenSchemaType} from '../utils/flattenSchemaType'\nimport {LAUNCHDARKLY_CONFIG_DEFAULT, LaunchDarklyProvider} from './components/LaunchDarklyContext'\nimport {LaunchDarklyFieldLevelConfig} from './types'\nimport {getExperiments} from './utils'\n\nexport const fieldLevelExperiments = definePlugin<LaunchDarklyFieldLevelConfig>((config) => {\n const pluginConfig = {...LAUNCHDARKLY_CONFIG_DEFAULT, ...config}\n const {fields, projectKey, tags} = pluginConfig\n return {\n name: 'sanity-growthbook-personalistaion-plugin-field-level-experiments',\n plugins: [\n baseFieldLevelExperiments({\n fields,\n experiments: (client) => getExperiments({client, projectKey, tags}),\n experimentNameOverride: 'flag',\n }),\n ],\n\n form: {\n components: {\n input: (props) => {\n const isRootInput = props.id === 'root' && isObjectInputProps(props)\n\n if (!isRootInput) {\n return props.renderDefault(props)\n }\n\n const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(\n (field) => field.type.name,\n )\n\n const hasExperiment = flatFieldTypeNames.some((name) => name.startsWith('flag'))\n\n if (!hasExperiment) {\n return props.renderDefault(props)\n }\n\n const providerProps = {\n ...props,\n launchDarklyFieldPluginConfig: {\n ...pluginConfig,\n },\n }\n return LaunchDarklyProvider(providerProps)\n },\n },\n },\n }\n})\n"],"names":["useSecrets","useState","useEffect","jsxs","Fragment","jsx","SettingsView","createContext","useContext","useMemo","definePlugin","baseFieldLevelExperiments","isObjectInputProps","flattenSchemaType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/launchDarkly/components/Secrets.tsx","../../src/launchDarkly/components/LaunchDarklyContext.tsx","../../src/launchDarkly/utils.ts","../../src/launchDarkly/index.ts"],"sourcesContent":["import {SettingsView, useSecrets} from '@sanity/studio-secrets'\nimport {useEffect, useState} from 'react'\nimport type {ObjectInputProps} from 'sanity'\n\nimport {useLaunchDarklyContext} from './LaunchDarklyContext'\n\nconst namespace = 'launchdarkly'\nconst pluginConfigKeys = [\n {\n key: 'apiKey',\n title: 'Your secret API key',\n },\n]\n\nexport const Secrets = (props: ObjectInputProps) => {\n const {secrets, loading} = useSecrets(namespace) as {secrets: {apiKey: string}; loading: boolean}\n const {setSecret} = useLaunchDarklyContext()\n const [showSettings, setShowSettings] = useState<boolean>(false)\n\n useEffect(() => {\n if (loading) return undefined\n if (!secrets && !loading) {\n setSecret(undefined)\n // oxlint-disable-next-line react/react-compiler\n return setShowSettings(true)\n }\n setSecret(secrets.apiKey)\n return setShowSettings(false)\n }, [secrets, loading, setSecret])\n\n if (!showSettings) {\n return props.renderDefault(props)\n }\n return (\n <>\n <SettingsView\n title={`${namespace} api key`}\n namespace={namespace}\n keys={pluginConfigKeys}\n onClose={() => {\n setShowSettings(false)\n }}\n />\n {props.renderDefault(props)}\n </>\n )\n}\n","import {createContext, useContext, useMemo, useState} from 'react'\nimport type {ObjectInputProps} from 'sanity'\n\nimport type {LaunchDarklyContextProps, LaunchDarklyFieldLevelConfig} from '../types'\nimport {Secrets} from './Secrets'\n\nexport const LAUNCHDARKLY_CONFIG_DEFAULT = {}\n\nconst LaunchDarklyContext = createContext<LaunchDarklyContextProps>({\n setSecret: () => undefined,\n secret: undefined,\n})\n\nexport function useLaunchDarklyContext() {\n return useContext(LaunchDarklyContext)\n}\n\ntype LaunchDarklyProps = ObjectInputProps & {\n launchDarklyFieldPluginConfig: LaunchDarklyFieldLevelConfig\n}\n\nexport function LaunchDarklyProvider(props: LaunchDarklyProps) {\n const {launchDarklyFieldPluginConfig} = props\n const [secret, setSecret] = useState<string | undefined>()\n\n const context = useMemo(\n () => ({...launchDarklyFieldPluginConfig, secret, setSecret}),\n [launchDarklyFieldPluginConfig, secret, setSecret],\n )\n\n return (\n <LaunchDarklyContext.Provider value={context}>\n <Secrets {...props} />\n </LaunchDarklyContext.Provider>\n )\n}\n","import type {SanityClient} from 'sanity'\n\nimport type {ExperimentType} from '../types'\nimport type {LaunchDarklyFieldLevelConfig, LaunchDarklyFlagItem} from './types'\n\nexport const getExperiments = async ({\n client,\n projectKey,\n tags,\n}: Omit<LaunchDarklyFieldLevelConfig, 'fields'> & {client: SanityClient}): Promise<\n ExperimentType[]\n> => {\n const query = `*[_id == 'secrets.launchdarkly'][0].secrets.apiKey`\n\n const secret = await client.fetch(query) // secret is stored in the content lake using @sanity/studio-secrets\n if (!secret) return []\n\n const url = new URL(`https://app.launchdarkly.com/api/v2/flags/${projectKey}`)\n\n if (tags) {\n url.searchParams.set('filter', `tags:${tags.join('+')}`)\n }\n\n const featureExperiments: ExperimentType[] = []\n let hasMore = true\n const offset = 0\n const limit = 10\n\n while (hasMore) {\n url.searchParams.set('offset', offset.toString())\n url.searchParams.set('limit', limit.toString())\n const responseFlags = await fetch(url, {\n headers: {\n Authorization: secret,\n },\n })\n\n const {items} = await responseFlags.json()\n const experiments = items.map((flag: LaunchDarklyFlagItem) => ({\n id: flag.key,\n label: flag.name,\n variants: flag.variations.map((variation) => ({\n id: variation.value.toString(),\n label: variation.name ?? variation.value.toString(),\n })),\n }))\n featureExperiments.push(...experiments)\n if (items.length !== limit) {\n hasMore = false\n }\n }\n\n return featureExperiments\n}\n","import {definePlugin, isObjectInputProps} from 'sanity'\n\nimport {fieldLevelExperiments as baseFieldLevelExperiments} from '../fieldExperiments'\nimport {flattenSchemaType} from '../utils/flattenSchemaType'\nimport {LAUNCHDARKLY_CONFIG_DEFAULT, LaunchDarklyProvider} from './components/LaunchDarklyContext'\nimport type {LaunchDarklyFieldLevelConfig} from './types'\nimport {getExperiments} from './utils'\n\nexport const fieldLevelExperiments = definePlugin<LaunchDarklyFieldLevelConfig>((config) => {\n const pluginConfig = {...LAUNCHDARKLY_CONFIG_DEFAULT, ...config}\n const {fields, projectKey, tags} = pluginConfig\n return {\n name: 'sanity-growthbook-personalistaion-plugin-field-level-experiments',\n plugins: [\n baseFieldLevelExperiments({\n fields,\n experiments: (client) => getExperiments({client, projectKey, tags}),\n experimentNameOverride: 'flag',\n }),\n ],\n\n form: {\n components: {\n input: (props) => {\n const isRootInput = props.id === 'root' && isObjectInputProps(props)\n\n if (!isRootInput) {\n return props.renderDefault(props)\n }\n\n const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(\n (field) => field.type.name,\n )\n\n const hasExperiment = flatFieldTypeNames.some((name) => name.startsWith('flag'))\n\n if (!hasExperiment) {\n return props.renderDefault(props)\n }\n\n const providerProps = {\n ...props,\n launchDarklyFieldPluginConfig: {\n ...pluginConfig,\n },\n }\n return LaunchDarklyProvider(providerProps)\n },\n },\n },\n }\n})\n"],"names":["namespace","pluginConfigKeys","key","title","Secrets","props","$","_c","secrets","loading","useSecrets","setSecret","useLaunchDarklyContext","showSettings","setShowSettings","useState","t0","t1","undefined","apiKey","useEffect","t2","renderDefault","for","t3","t4","LAUNCHDARKLY_CONFIG_DEFAULT","LaunchDarklyContext","createContext","secret","useContext","LaunchDarklyProvider","launchDarklyFieldPluginConfig","context","getExperiments","client","projectKey","tags","fetch","url","URL","searchParams","set","join","featureExperiments","hasMore","offset","limit","toString","responseFlags","headers","Authorization","items","json","experiments","map","flag","id","label","name","variants","variations","variation","value","push","length","fieldLevelExperiments","definePlugin","config","pluginConfig","fields","plugins","baseFieldLevelExperiments","experimentNameOverride","form","components","input","isObjectInputProps","flattenSchemaType","schemaType","field","type","some","startsWith","providerProps"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,104 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/personalization-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Plugin to help with personalization, a/b testing when using Sanity",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
7
7
|
"sanity-plugin"
|
|
8
8
|
],
|
|
9
|
-
"homepage": "https://github.com/sanity-io/sanity-plugin
|
|
9
|
+
"homepage": "https://github.com/sanity-io/plugins/tree/main/plugins/@sanity/personalization-plugin#readme",
|
|
10
10
|
"bugs": {
|
|
11
|
-
"url": "https://github.com/sanity-io/
|
|
11
|
+
"url": "https://github.com/sanity-io/plugins/issues"
|
|
12
12
|
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Sanity.io <hello@sanity.io>",
|
|
13
15
|
"repository": {
|
|
14
16
|
"type": "git",
|
|
15
|
-
"url": "git@github.com
|
|
16
|
-
|
|
17
|
-
"license": "MIT",
|
|
18
|
-
"author": "Sanity <hello@sanity.io>",
|
|
19
|
-
"sideEffects": false,
|
|
20
|
-
"type": "commonjs",
|
|
21
|
-
"exports": {
|
|
22
|
-
".": {
|
|
23
|
-
"source": "./src/index.ts",
|
|
24
|
-
"import": "./dist/index.mjs",
|
|
25
|
-
"default": "./dist/index.js"
|
|
26
|
-
},
|
|
27
|
-
"./launchDarkly": {
|
|
28
|
-
"source": "./src/launchDarkly/index.ts",
|
|
29
|
-
"import": "./dist/launchDarkly/index.mjs",
|
|
30
|
-
"default": "./dist/launchDarkly/index.js"
|
|
31
|
-
},
|
|
32
|
-
"./growthbook": {
|
|
33
|
-
"source": "./src/growthbook/index.ts",
|
|
34
|
-
"import": "./dist/growthbook/index.mjs",
|
|
35
|
-
"default": "./dist/growthbook/index.js"
|
|
36
|
-
},
|
|
37
|
-
"./package.json": "./package.json"
|
|
17
|
+
"url": "git+ssh://git@github.com/sanity-io/plugins.git",
|
|
18
|
+
"directory": "plugins/@sanity/personalization-plugin"
|
|
38
19
|
},
|
|
39
|
-
"main": "./dist/index.js",
|
|
40
|
-
"types": "./dist/index.d.ts",
|
|
41
20
|
"files": [
|
|
42
|
-
"dist"
|
|
43
|
-
"sanity.json",
|
|
44
|
-
"src",
|
|
45
|
-
"v2-incompatible.js"
|
|
21
|
+
"dist"
|
|
46
22
|
],
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"prepare": "husky"
|
|
23
|
+
"type": "module",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./dist/index.js",
|
|
27
|
+
"./launchDarkly": "./dist/launchDarkly/index.js",
|
|
28
|
+
"./growthbook": "./dist/growthbook/index.js",
|
|
29
|
+
"./package.json": "./package.json"
|
|
55
30
|
},
|
|
56
31
|
"dependencies": {
|
|
57
|
-
"@sanity/
|
|
58
|
-
"@sanity/
|
|
59
|
-
"@sanity/ui": "^2.16.12",
|
|
32
|
+
"@sanity/icons": "^3.7.4",
|
|
33
|
+
"@sanity/ui": "^3.2.0",
|
|
60
34
|
"@sanity/uuid": "^3.0.2",
|
|
61
35
|
"fast-deep-equal": "^3.1.3",
|
|
62
36
|
"react-icons": "^5.5.0",
|
|
63
|
-
"suspend-react": "^0.1.3"
|
|
37
|
+
"suspend-react": "^0.1.3",
|
|
38
|
+
"@sanity/studio-secrets": "4.0.5"
|
|
64
39
|
},
|
|
65
40
|
"devDependencies": {
|
|
66
|
-
"@
|
|
67
|
-
"@
|
|
68
|
-
"@
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"@
|
|
74
|
-
"
|
|
75
|
-
"eslint-config-prettier": "^9.1.2",
|
|
76
|
-
"eslint-config-sanity": "^7.1.4",
|
|
77
|
-
"eslint-plugin-prettier": "^5.5.4",
|
|
78
|
-
"eslint-plugin-react": "^7.37.5",
|
|
79
|
-
"eslint-plugin-react-hooks": "^5.2.0",
|
|
80
|
-
"husky": "^9.1.7",
|
|
81
|
-
"lint-staged": "^15.2.10",
|
|
82
|
-
"prettier": "^3.6.2",
|
|
83
|
-
"prettier-plugin-packagejson": "^2.5.19",
|
|
84
|
-
"react": "^19.2.3",
|
|
85
|
-
"react-dom": "^19.2.3",
|
|
86
|
-
"sanity": "^5.0.1",
|
|
87
|
-
"semantic-release": "^24.2.7",
|
|
88
|
-
"styled-components": "^6.1.19",
|
|
89
|
-
"typescript": "^5.9.2"
|
|
41
|
+
"@sanity/pkg-utils": "^10.5.7",
|
|
42
|
+
"@types/react": "^19.2.17",
|
|
43
|
+
"@types/react-dom": "^19.2.3",
|
|
44
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
45
|
+
"react": "^19.2.7",
|
|
46
|
+
"react-dom": "^19.2.7",
|
|
47
|
+
"sanity": "^6.1.0",
|
|
48
|
+
"@repo/package.config": "0.0.0",
|
|
49
|
+
"@repo/tsconfig": "0.0.0"
|
|
90
50
|
},
|
|
91
51
|
"peerDependencies": {
|
|
92
|
-
"react": "^
|
|
93
|
-
"
|
|
52
|
+
"react": "^19.2",
|
|
53
|
+
"react-dom": "^19.2",
|
|
54
|
+
"sanity": "^5 || ^6.0.0-0"
|
|
94
55
|
},
|
|
95
56
|
"engines": {
|
|
96
|
-
"node": ">=
|
|
97
|
-
},
|
|
98
|
-
"resolutions": {
|
|
99
|
-
"conventional-changelog-conventionalcommits": ">= 8.0.0"
|
|
57
|
+
"node": ">=20.19 <22 || >=22.12"
|
|
100
58
|
},
|
|
101
|
-
"
|
|
102
|
-
"
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "pkg build --strict --check --clean"
|
|
103
61
|
}
|
|
104
|
-
}
|
|
62
|
+
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {FieldDefinition} from 'sanity'
|
|
2
|
-
import {Plugin as Plugin_2} from 'sanity'
|
|
3
|
-
|
|
4
|
-
export declare const fieldLevelExperiments: Plugin_2<GrowthbookExperimentFieldPluginConfig>
|
|
5
|
-
|
|
6
|
-
declare type GrowthbookExperimentFieldPluginConfig = {
|
|
7
|
-
fields: (string | FieldDefinition)[]
|
|
8
|
-
environment: string
|
|
9
|
-
baseUrl?: string
|
|
10
|
-
project?: string
|
|
11
|
-
convertBooleans?: boolean
|
|
12
|
-
tags?: string[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export {}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { definePlugin, isObjectInputProps } from "sanity";
|
|
2
|
-
import { fieldLevelExperiments as fieldLevelExperiments$1, flattenSchemaType } from "../index.mjs";
|
|
3
|
-
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
-
import { useState, useEffect, createContext, useMemo, useContext } from "react";
|
|
5
|
-
import { useSecrets, SettingsView } from "@sanity/studio-secrets";
|
|
6
|
-
const namespace = "growthbook", pluginConfigKeys = [
|
|
7
|
-
{
|
|
8
|
-
key: "apiKey",
|
|
9
|
-
title: "Your secret API key"
|
|
10
|
-
}
|
|
11
|
-
], Secrets = (props) => {
|
|
12
|
-
const { secrets, loading } = useSecrets(namespace), { setSecret } = useGrowthbookContext(), [showSettings, setShowSettings] = useState(!1);
|
|
13
|
-
return useEffect(() => {
|
|
14
|
-
if (!loading)
|
|
15
|
-
return !secrets && !loading ? (setSecret(void 0), setShowSettings(!0)) : (setSecret(secrets.apiKey), setShowSettings(!1));
|
|
16
|
-
}, [secrets, loading, setSecret]), showSettings ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17
|
-
/* @__PURE__ */ jsx(
|
|
18
|
-
SettingsView,
|
|
19
|
-
{
|
|
20
|
-
title: "Growthbook secret",
|
|
21
|
-
namespace,
|
|
22
|
-
keys: pluginConfigKeys,
|
|
23
|
-
onClose: () => {
|
|
24
|
-
setShowSettings(!1);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
),
|
|
28
|
-
props.renderDefault(props)
|
|
29
|
-
] }) : props.renderDefault(props);
|
|
30
|
-
}, GROWTHBOOK_CONFIG_DEFAULT = {
|
|
31
|
-
baseUrl: "https://api.growthbook.io/api/v1"
|
|
32
|
-
}, GrowthbookContext = createContext({
|
|
33
|
-
setSecret: () => {
|
|
34
|
-
},
|
|
35
|
-
secret: void 0
|
|
36
|
-
});
|
|
37
|
-
function useGrowthbookContext() {
|
|
38
|
-
return useContext(GrowthbookContext);
|
|
39
|
-
}
|
|
40
|
-
function GrowthbookProvider(props) {
|
|
41
|
-
const { growthbookFieldPluginConfig } = props, [secret, setSecret] = useState(), context = useMemo(
|
|
42
|
-
() => ({ ...growthbookFieldPluginConfig, secret, setSecret }),
|
|
43
|
-
[growthbookFieldPluginConfig, secret, setSecret]
|
|
44
|
-
);
|
|
45
|
-
return /* @__PURE__ */ jsx(GrowthbookContext.Provider, { value: context, children: /* @__PURE__ */ jsx(Secrets, { ...props }) });
|
|
46
|
-
}
|
|
47
|
-
const getBooleanConversion = (value) => value === "true" ? "variant" : value === "false" ? "control" : value, getExperiments = async ({
|
|
48
|
-
client,
|
|
49
|
-
environment,
|
|
50
|
-
baseUrl,
|
|
51
|
-
project,
|
|
52
|
-
convertBooleans,
|
|
53
|
-
tags
|
|
54
|
-
}) => {
|
|
55
|
-
const query = `*[_id == 'secrets.${namespace}'][0].secrets.${pluginConfigKeys[0].key}`, secret = await client.fetch(query);
|
|
56
|
-
if (!secret) return [];
|
|
57
|
-
const featureExperiments = [];
|
|
58
|
-
let hasMore = !0, offset = 0;
|
|
59
|
-
const url = new URL(`${baseUrl}/features`);
|
|
60
|
-
for (project && url.searchParams.set("projectId", project); hasMore; ) {
|
|
61
|
-
url.searchParams.set("offset", offset.toString());
|
|
62
|
-
const response = await fetch(url, {
|
|
63
|
-
headers: {
|
|
64
|
-
Authorization: `Bearer ${secret}`
|
|
65
|
-
}
|
|
66
|
-
}), { features, hasMore: responseHasMore, nextOffset } = await response.json();
|
|
67
|
-
hasMore = responseHasMore, offset = nextOffset, features && features.forEach((feature) => {
|
|
68
|
-
if (feature.archived || tags && feature.tags && !feature.tags.some((tag) => tags.includes(tag)))
|
|
69
|
-
return;
|
|
70
|
-
const experiments = feature.environments[environment]?.rules.filter(
|
|
71
|
-
(experiment) => experiment.type === "experiment-ref" || experiment.type === "experiment"
|
|
72
|
-
);
|
|
73
|
-
if (!experiments)
|
|
74
|
-
return;
|
|
75
|
-
const variations = [], uniqueValues = /* @__PURE__ */ new Set();
|
|
76
|
-
experiments.forEach((experiment) => {
|
|
77
|
-
experiment?.variations.forEach((variant) => {
|
|
78
|
-
const value2 = convertBooleans ? getBooleanConversion(variant.value) : variant.value;
|
|
79
|
-
uniqueValues.has(value2) || (uniqueValues.add(value2), variations.push({
|
|
80
|
-
id: value2,
|
|
81
|
-
label: value2
|
|
82
|
-
}));
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
const value = { id: feature.id, label: feature.id, variants: variations };
|
|
86
|
-
featureExperiments.push(value);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
return featureExperiments.sort((a, b) => a.id.localeCompare(b.id));
|
|
90
|
-
}, fieldLevelExperiments = definePlugin(
|
|
91
|
-
(config) => {
|
|
92
|
-
const pluginConfig = { ...GROWTHBOOK_CONFIG_DEFAULT, ...config }, { fields, environment, project, convertBooleans, baseUrl, tags } = pluginConfig;
|
|
93
|
-
return {
|
|
94
|
-
name: "sanity-growthbook-personalistaion-plugin-field-level-experiments",
|
|
95
|
-
plugins: [
|
|
96
|
-
fieldLevelExperiments$1({
|
|
97
|
-
fields,
|
|
98
|
-
experiments: (client) => getExperiments({ client, environment, baseUrl, project, convertBooleans, tags })
|
|
99
|
-
})
|
|
100
|
-
],
|
|
101
|
-
form: {
|
|
102
|
-
components: {
|
|
103
|
-
input: (props) => {
|
|
104
|
-
if (!(props.id === "root" && isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map(
|
|
105
|
-
(field) => field.type.name
|
|
106
|
-
).some((name) => name.startsWith("experiment")))
|
|
107
|
-
return props.renderDefault(props);
|
|
108
|
-
const providerProps = {
|
|
109
|
-
...props,
|
|
110
|
-
growthbookFieldPluginConfig: {
|
|
111
|
-
...pluginConfig
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
return GrowthbookProvider(providerProps);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
);
|
|
121
|
-
export {
|
|
122
|
-
fieldLevelExperiments
|
|
123
|
-
};
|
|
124
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../src/growthbook/Components/Secrets.tsx","../../src/growthbook/Components/GrowthbookContext.tsx","../../src/growthbook/utils.ts","../../src/growthbook/index.ts"],"sourcesContent":["import {SettingsView, useSecrets} from '@sanity/studio-secrets'\nimport {useEffect, useState} from 'react'\nimport {ObjectInputProps} from 'sanity'\n\nimport {useGrowthbookContext} from './GrowthbookContext'\n\nexport const namespace = 'growthbook'\n\nexport const pluginConfigKeys = [\n {\n key: 'apiKey',\n title: 'Your secret API key',\n },\n]\n\nexport const Secrets = (props: ObjectInputProps) => {\n const {secrets, loading} = useSecrets(namespace) as {secrets: {apiKey: string}; loading: boolean}\n const {setSecret} = useGrowthbookContext()\n const [showSettings, setShowSettings] = useState<boolean>(false)\n\n useEffect(() => {\n if (loading) return undefined\n if (!secrets && !loading) {\n setSecret(undefined)\n return setShowSettings(true)\n }\n setSecret(secrets.apiKey)\n return setShowSettings(false)\n }, [secrets, loading, setSecret])\n\n if (!showSettings) {\n return props.renderDefault(props)\n }\n return (\n <>\n <SettingsView\n title={'Growthbook secret'}\n namespace={namespace}\n keys={pluginConfigKeys}\n onClose={() => {\n setShowSettings(false)\n }}\n />\n {props.renderDefault(props)}\n </>\n )\n}\n","import {createContext, useContext, useMemo, useState} from 'react'\nimport {ObjectInputProps} from 'sanity'\n\nimport {GrowthbookContextProps, GrowthbookExperimentFieldPluginConfig} from '../types'\nimport {Secrets} from './Secrets'\n\nexport const GROWTHBOOK_CONFIG_DEFAULT = {\n baseUrl: 'https://api.growthbook.io/api/v1',\n}\n\nexport const GrowthbookContext = createContext<GrowthbookContextProps>({\n setSecret: () => undefined,\n secret: undefined,\n})\n\nexport function useGrowthbookContext() {\n return useContext(GrowthbookContext)\n}\n\ntype GrowthbookProps = ObjectInputProps & {\n growthbookFieldPluginConfig: GrowthbookExperimentFieldPluginConfig\n}\n\nexport function GrowthbookProvider(props: GrowthbookProps) {\n const {growthbookFieldPluginConfig} = props\n const [secret, setSecret] = useState<string | undefined>()\n\n const context = useMemo(\n () => ({...growthbookFieldPluginConfig, secret, setSecret}),\n [growthbookFieldPluginConfig, secret, setSecret],\n )\n\n return (\n <GrowthbookContext.Provider value={context}>\n <Secrets {...props} />\n </GrowthbookContext.Provider>\n )\n}\n","import {SanityClient} from 'sanity'\n\nimport {ExperimentType, GrowthbookFeature, VariantType} from '../types'\nimport {namespace, pluginConfigKeys} from './Components/Secrets'\nimport {GrowthbookExperimentFieldPluginConfig} from './types'\n\nconst getBooleanConversion = (value: string) => {\n // control is false\n if (value === 'true') {\n return 'variant'\n } else if (value === 'false') {\n return 'control'\n }\n return value\n}\n\nexport const getExperiments = async ({\n client,\n environment,\n baseUrl,\n project,\n convertBooleans,\n tags,\n}: Omit<GrowthbookExperimentFieldPluginConfig, 'fields' | 'baseUrl'> & {\n client: SanityClient\n baseUrl: string\n}): Promise<ExperimentType[]> => {\n const query = `*[_id == 'secrets.${namespace}'][0].secrets.${pluginConfigKeys[0].key}`\n\n const secret = await client.fetch(query) // secret is stored in the content lake using @sanity/studio-secrets\n if (!secret) return []\n\n const featureExperiments: ExperimentType[] = []\n let hasMore = true\n let offset = 0\n const url = new URL(`${baseUrl}/features`)\n if (project) {\n url.searchParams.set('projectId', project)\n }\n\n while (hasMore) {\n url.searchParams.set('offset', offset.toString())\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${secret}`,\n },\n })\n\n const {features, hasMore: responseHasMore, nextOffset} = await response.json()\n\n hasMore = responseHasMore\n offset = nextOffset\n if (!features) continue\n\n features.forEach((feature: GrowthbookFeature) => {\n if (feature.archived) {\n return undefined\n }\n if (tags && feature.tags && !feature.tags.some((tag) => tags.includes(tag))) {\n return undefined\n }\n\n const experiments = feature.environments[environment]?.rules.filter(\n (experiment) => experiment.type === 'experiment-ref' || experiment.type === 'experiment',\n )\n\n if (!experiments) {\n return undefined\n }\n\n const variations: VariantType[] = []\n const uniqueValues = new Set<string>()\n\n experiments.forEach((experiment) => {\n experiment?.variations.forEach((variant) => {\n const value = convertBooleans ? getBooleanConversion(variant.value) : variant.value\n if (!uniqueValues.has(value)) {\n uniqueValues.add(value)\n variations.push({\n id: value,\n label: value,\n })\n }\n })\n })\n const value = {id: feature.id, label: feature.id, variants: variations}\n\n featureExperiments.push(value)\n return undefined\n })\n }\n const sortedFeatureExperiments = featureExperiments.sort((a, b) => a.id.localeCompare(b.id))\n return sortedFeatureExperiments\n}\n","import {definePlugin, isObjectInputProps} from 'sanity'\n\nimport {fieldLevelExperiments as baseFieldLevelExperiments} from '../fieldExperiments'\nimport {flattenSchemaType} from '../utils/flattenSchemaType'\nimport {GROWTHBOOK_CONFIG_DEFAULT, GrowthbookProvider} from './Components/GrowthbookContext'\nimport {GrowthbookExperimentFieldPluginConfig} from './types'\nimport {getExperiments} from './utils'\n\nexport const fieldLevelExperiments = definePlugin<GrowthbookExperimentFieldPluginConfig>(\n (config) => {\n const pluginConfig = {...GROWTHBOOK_CONFIG_DEFAULT, ...config}\n const {fields, environment, project, convertBooleans, baseUrl, tags} = pluginConfig\n return {\n name: 'sanity-growthbook-personalistaion-plugin-field-level-experiments',\n plugins: [\n baseFieldLevelExperiments({\n fields,\n experiments: (client) =>\n getExperiments({client, environment, baseUrl, project, convertBooleans, tags}),\n }),\n ],\n\n form: {\n components: {\n input: (props) => {\n const isRootInput = props.id === 'root' && isObjectInputProps(props)\n\n if (!isRootInput) {\n return props.renderDefault(props)\n }\n\n const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(\n (field) => field.type.name,\n )\n\n const hasExperiment = flatFieldTypeNames.some((name) => name.startsWith('experiment'))\n\n if (!hasExperiment) {\n return props.renderDefault(props)\n }\n\n const providerProps = {\n ...props,\n growthbookFieldPluginConfig: {\n ...pluginConfig,\n },\n }\n return GrowthbookProvider(providerProps)\n },\n },\n },\n }\n },\n)\n"],"names":["value","baseFieldLevelExperiments"],"mappings":";;;;;AAMO,MAAM,YAAY,cAEZ,mBAAmB;AAAA,EAC9B;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,EAAA;AAEX,GAEa,UAAU,CAAC,UAA4B;AAClD,QAAM,EAAC,SAAS,QAAA,IAAW,WAAW,SAAS,GACzC,EAAC,UAAA,IAAa,wBACd,CAAC,cAAc,eAAe,IAAI,SAAkB,EAAK;AAY/D,SAVA,UAAU,MAAM;AACd,QAAI,CAAA;AACJ,aAAI,CAAC,WAAW,CAAC,WACf,UAAU,MAAS,GACZ,gBAAgB,EAAI,MAE7B,UAAU,QAAQ,MAAM,GACjB,gBAAgB,EAAK;AAAA,EAC9B,GAAG,CAAC,SAAS,SAAS,SAAS,CAAC,GAE3B,eAIH,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA,MAAM;AAAA,QACN,SAAS,MAAM;AACb,0BAAgB,EAAK;AAAA,QACvB;AAAA,MAAA;AAAA,IAAA;AAAA,IAED,MAAM,cAAc,KAAK;AAAA,EAAA,EAAA,CAC5B,IAbO,MAAM,cAAc,KAAK;AAepC,GCxCa,4BAA4B;AAAA,EACvC,SAAS;AACX,GAEa,oBAAoB,cAAsC;AAAA,EACrE,WAAW,MAAG;AAAA,EAAA;AAAA,EACd,QAAQ;AACV,CAAC;AAEM,SAAS,uBAAuB;AACrC,SAAO,WAAW,iBAAiB;AACrC;AAMO,SAAS,mBAAmB,OAAwB;AACzD,QAAM,EAAC,gCAA+B,OAChC,CAAC,QAAQ,SAAS,IAAI,YAEtB,UAAU;AAAA,IACd,OAAO,EAAC,GAAG,6BAA6B,QAAQ,UAAA;AAAA,IAChD,CAAC,6BAA6B,QAAQ,SAAS;AAAA,EAAA;AAGjD,SACE,oBAAC,kBAAkB,UAAlB,EAA2B,OAAO,SACjC,UAAA,oBAAC,SAAA,EAAS,GAAG,MAAA,CAAO,EAAA,CACtB;AAEJ;AC/BA,MAAM,uBAAuB,CAAC,UAExB,UAAU,SACL,YACE,UAAU,UACZ,YAEF,OAGI,iBAAiB,OAAO;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAGiC;AAC/B,QAAM,QAAQ,qBAAqB,SAAS,iBAAiB,iBAAiB,CAAC,EAAE,GAAG,IAE9E,SAAS,MAAM,OAAO,MAAM,KAAK;AACvC,MAAI,CAAC,OAAQ,QAAO,CAAA;AAEpB,QAAM,qBAAuC,CAAA;AAC7C,MAAI,UAAU,IACV,SAAS;AACb,QAAM,MAAM,IAAI,IAAI,GAAG,OAAO,WAAW;AAKzC,OAJI,WACF,IAAI,aAAa,IAAI,aAAa,OAAO,GAGpC,WAAS;AACd,QAAI,aAAa,IAAI,UAAU,OAAO,UAAU;AAChD,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,MAAA;AAAA,IACjC,CACD,GAEK,EAAC,UAAU,SAAS,iBAAiB,eAAc,MAAM,SAAS,KAAA;AAExE,cAAU,iBACV,SAAS,YACJ,YAEL,SAAS,QAAQ,CAAC,YAA+B;AAI/C,UAHI,QAAQ,YAGR,QAAQ,QAAQ,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,KAAK,SAAS,GAAG,CAAC;AACxE;AAGF,YAAM,cAAc,QAAQ,aAAa,WAAW,GAAG,MAAM;AAAA,QAC3D,CAAC,eAAe,WAAW,SAAS,oBAAoB,WAAW,SAAS;AAAA,MAAA;AAG9E,UAAI,CAAC;AACH;AAGF,YAAM,aAA4B,CAAA,GAC5B,mCAAmB,IAAA;AAEzB,kBAAY,QAAQ,CAAC,eAAe;AAClC,oBAAY,WAAW,QAAQ,CAAC,YAAY;AAC1C,gBAAMA,SAAQ,kBAAkB,qBAAqB,QAAQ,KAAK,IAAI,QAAQ;AACzE,uBAAa,IAAIA,MAAK,MACzB,aAAa,IAAIA,MAAK,GACtB,WAAW,KAAK;AAAA,YACd,IAAIA;AAAAA,YACJ,OAAOA;AAAAA,UAAA,CACR;AAAA,QAEL,CAAC;AAAA,MACH,CAAC;AACD,YAAM,QAAQ,EAAC,IAAI,QAAQ,IAAI,OAAO,QAAQ,IAAI,UAAU,WAAA;AAE5D,yBAAmB,KAAK,KAAK;AAAA,IAE/B,CAAC;AAAA,EACH;AAEA,SADiC,mBAAmB,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAE7F,GCrFa,wBAAwB;AAAA,EACnC,CAAC,WAAW;AACV,UAAM,eAAe,EAAC,GAAG,2BAA2B,GAAG,OAAA,GACjD,EAAC,QAAQ,aAAa,SAAS,iBAAiB,SAAS,SAAQ;AACvE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,QACPC,wBAA0B;AAAA,UACxB;AAAA,UACA,aAAa,CAAC,WACZ,eAAe,EAAC,QAAQ,aAAa,SAAS,SAAS,iBAAiB,KAAA,CAAK;AAAA,QAAA,CAChF;AAAA,MAAA;AAAA,MAGH,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,OAAO,CAAC,UAAU;AAahB,gBAVI,EAFgB,MAAM,OAAO,UAAU,mBAAmB,KAAK,MAY/D,CANuB,kBAAkB,MAAM,UAAU,EAAE;AAAA,cAC7D,CAAC,UAAU,MAAM,KAAK;AAAA,YAAA,EAGiB,KAAK,CAAC,SAAS,KAAK,WAAW,YAAY,CAAC;AAGnF,qBAAO,MAAM,cAAc,KAAK;AAGlC,kBAAM,gBAAgB;AAAA,cACpB,GAAG;AAAA,cACH,6BAA6B;AAAA,gBAC3B,GAAG;AAAA,cAAA;AAAA,YACL;AAEF,mBAAO,mBAAmB,aAAa;AAAA,UACzC;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EAEJ;AACF;"}
|