@sanity/personalization-plugin 2.1.0-field-names.1 → 2.1.0-growthbook.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/personalization-plugin",
3
- "version": "2.1.0-field-names.1",
3
+ "version": "2.1.0-growthbook.1",
4
4
  "description": "Plugin to help with personalization, a/b testing when using Sanity",
5
5
  "keywords": [
6
6
  "sanity",
@@ -45,6 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@sanity/incompatible-plugin": "^1.0.4",
48
+ "@sanity/studio-secrets": "^3.0.0",
48
49
  "@sanity/ui": "^2.8.19",
49
50
  "@sanity/uuid": "^3.0.2",
50
51
  "fast-deep-equal": "^3.1.3",
@@ -8,43 +8,44 @@ import {useExperimentContext} from './ExperimentContext'
8
8
 
9
9
  export const ArrayInput = (props: ArrayInputProps) => {
10
10
  const fieldPath = props.path.slice(0, -1)
11
- const {onItemAppend, variantName, variantId, experimentId} = props
12
- const experimentValue = useFormValue([...fieldPath, experimentId])
11
+ const experimentId = useFormValue([...fieldPath, 'experimentId'])
13
12
 
14
13
  const {experiments} = useExperimentContext()
15
14
 
15
+ const {onItemAppend, objectName} = props
16
+
16
17
  const handleClick = useCallback(
17
18
  async (variant: VariantType) => {
18
19
  const item = {
19
20
  _key: uuid(),
20
- [variantId]: variant.id,
21
- [experimentId]: experimentValue,
22
- _type: variantName,
21
+ variantId: variant.id,
22
+ experimentId: experimentId,
23
+ _type: objectName,
23
24
  }
24
25
 
25
26
  // Patch the document
26
27
  onItemAppend(item)
27
28
  },
28
- [variantId, experimentId, experimentValue, variantName, onItemAppend],
29
+ [experimentId, objectName, onItemAppend],
29
30
  )
30
31
 
31
32
  const filteredVariants =
32
33
  experiments.find((option) => {
33
- return option.id === experimentValue
34
+ return option.id === experimentId
34
35
  })?.variants || []
35
36
 
36
37
  type Value = {
38
+ experimentId: string
37
39
  value?: unknown
38
- [key: string]: string | unknown
39
40
  variantId: string
40
41
  _key: string
41
42
  _type: string
42
43
  }
43
44
 
44
45
  // there is probably some better was of getting the type of this?
45
- const values = (props.value as Value[]) || []
46
+ const values = props.value as Value[] | []
46
47
 
47
- const usedVariants = values?.map((variant) => variant[variantId])
48
+ const usedVariants = values?.map((variant) => variant.variantId)
48
49
 
49
50
  return (
50
51
  <Stack space={3}>
@@ -54,7 +55,7 @@ export const ArrayInput = (props: ArrayInputProps) => {
54
55
  {filteredVariants.map((variant) => {
55
56
  return (
56
57
  <Button
57
- key={`${experimentValue}-${variant.id}`}
58
+ key={`${experimentId}-${variant.id}`}
58
59
  text={`Add ${variant.label}`}
59
60
  mode="ghost"
60
61
  disabled={usedVariants?.includes(variant.id)}
@@ -1,5 +1,5 @@
1
1
  import equal from 'fast-deep-equal'
2
- import {createContext, useContext, useMemo} from 'react'
2
+ import {createContext, useContext, useMemo, useState} from 'react'
3
3
  import {type ObjectInputProps, useClient, useWorkspace} from 'sanity'
4
4
  import {suspend} from 'suspend-react'
5
5
 
@@ -11,16 +11,13 @@ import {ExperimentContextProps, FieldPluginConfig} from '../types'
11
11
  export const CONFIG_DEFAULT = {
12
12
  fields: [],
13
13
  apiVersion: '2024-11-07',
14
- experimentNameOverride: 'experiment',
15
- variantNameOverride: 'variant',
16
- variantId: 'variantId',
17
- variantArrayName: 'variants',
18
- experimentId: 'experimentId',
19
14
  }
20
15
 
21
16
  export const ExperimentContext = createContext<ExperimentContextProps>({
22
17
  ...CONFIG_DEFAULT,
23
18
  experiments: [],
19
+ setSecret: () => undefined,
20
+ secret: undefined,
24
21
  })
25
22
 
26
23
  export function useExperimentContext() {
@@ -33,6 +30,7 @@ type ExperimentProps = ObjectInputProps & {
33
30
 
34
31
  export function ExperimentProvider(props: ExperimentProps) {
35
32
  const {experimentFieldPluginConfig} = props
33
+ const [secret, setSecret] = useState<string | undefined>()
36
34
 
37
35
  const client = useClient({apiVersion: experimentFieldPluginConfig.apiVersion})
38
36
  const workspace = useWorkspace()
@@ -44,17 +42,17 @@ export function ExperimentProvider(props: ExperimentProps) {
44
42
  // eslint-disable-next-line require-await
45
43
  async () => {
46
44
  if (typeof experimentFieldPluginConfig.experiments === 'function') {
47
- return experimentFieldPluginConfig.experiments(client)
45
+ return experimentFieldPluginConfig.experiments(client, secret)
48
46
  }
49
47
  return experimentFieldPluginConfig.experiments
50
48
  },
51
- [workspace],
49
+ [workspace, secret],
52
50
  {equal},
53
51
  )
54
52
 
55
53
  const context = useMemo(
56
- () => ({...experimentFieldPluginConfig, experiments}),
57
- [experimentFieldPluginConfig, experiments],
54
+ () => ({...experimentFieldPluginConfig, experiments, secret, setSecret}),
55
+ [experimentFieldPluginConfig, experiments, secret, setSecret],
58
56
  )
59
57
 
60
58
  return (
@@ -1,4 +1,5 @@
1
1
  import {CloseIcon} from '@sanity/icons'
2
+ import {useCallback, useMemo} from 'react'
2
3
  import {GiSoapExperiment} from 'react-icons/gi'
3
4
  import {
4
5
  defineDocumentFieldAction,
@@ -13,105 +14,71 @@ import {
13
14
  type PatchStuff = {onChange: (patch: FormPatch | FormPatch[] | PatchEvent) => void; inputId: string}
14
15
 
15
16
  const useAddExperimentAction = (
16
- props: DocumentFieldActionProps &
17
- PatchStuff & {experimentNameOverride: string; experimentId: string; active: boolean},
17
+ props: DocumentFieldActionProps & PatchStuff,
18
18
  ): DocumentFieldActionItem => {
19
- const {onChange, active, experimentNameOverride} = props
20
-
21
- const handleAddAction = () => {
22
- // console.log('showing experiment', patchActiveTrueEvent)
23
- onChange([set(!active, ['active'])])
24
- }
19
+ const patchActiveEvent = useMemo(() => {
20
+ return set(true, ['active'])
21
+ }, [])
22
+ const handleAction = useCallback(() => {
23
+ props.onChange([patchActiveEvent])
24
+ }, [patchActiveEvent, props])
25
25
 
26
26
  return {
27
- title: `Add ${experimentNameOverride}`,
27
+ title: 'Add experiment',
28
28
  type: 'action',
29
29
  icon: GiSoapExperiment,
30
- onAction: handleAddAction,
30
+ onAction: handleAction,
31
31
  renderAsButton: true,
32
32
  }
33
33
  }
34
34
 
35
35
  const useRemoveExperimentAction = (
36
- props: DocumentFieldActionProps &
37
- PatchStuff & {experimentNameOverride: string; experimentId: string; active: boolean},
36
+ props: DocumentFieldActionProps & PatchStuff,
38
37
  ): DocumentFieldActionItem => {
39
- const {onChange, active, experimentId, experimentNameOverride} = props
40
- const patchActiveFalseEvent = () => {
38
+ const patchActiveEvent = useMemo(() => {
41
39
  const activeId = ['active']
42
- return set(!active, activeId)
43
- }
44
- const patchClearEvent = () => {
45
- const experiment = [experimentId]
46
- const variants = [experimentNameOverride]
47
- return [unset(experiment), unset(variants)]
48
- }
49
- const handleClearAction = () => {
50
- const clearEvents = patchClearEvent()
51
- const activeEvent = patchActiveFalseEvent()
52
- onChange([activeEvent, ...clearEvents])
53
- }
40
+ return set(false, activeId)
41
+ }, [])
42
+
43
+ const patchClearEvent = useMemo(() => {
44
+ const experimentId = ['experimentId'] // `${props.inputId}.experimentId`
45
+ const variants = ['variants'] //`${props.inputId}.variants`
46
+ return [unset(experimentId), unset(variants)]
47
+ }, [])
48
+ const handleAction = useCallback(() => {
49
+ props.onChange([patchActiveEvent, ...patchClearEvent])
50
+ }, [patchActiveEvent, patchClearEvent, props])
51
+
54
52
  return {
55
- title: `Remove ${experimentNameOverride}`,
53
+ title: 'Remove experiment',
56
54
  type: 'action',
57
55
  icon: CloseIcon,
58
- onAction: handleClearAction,
56
+ onAction: handleAction,
59
57
  renderAsButton: true,
60
58
  }
61
59
  }
62
60
 
63
- const newActions = ({
64
- onChange,
65
- inputId,
66
- active,
67
- experimentNameOverride,
68
- experimentId,
69
- }: PatchStuff & {active?: boolean; experimentNameOverride: string; experimentId: string}) => {
70
- const removeAction = defineDocumentFieldAction({
71
- name: `Remove ${experimentNameOverride}`,
72
- useAction: (props) =>
73
- useRemoveExperimentAction({
74
- ...props,
75
- active: true,
76
- onChange,
77
- inputId,
78
- experimentNameOverride,
79
- experimentId,
80
- }),
81
- })
82
- const addAction = defineDocumentFieldAction({
83
- name: `Add ${experimentNameOverride}`,
84
- useAction: (props) =>
85
- useAddExperimentAction({
86
- ...props,
87
- active: false,
88
- onChange,
89
- inputId,
90
- experimentNameOverride,
91
- experimentId,
92
- }),
93
- })
94
- if (active) {
95
- return removeAction
96
- }
97
- return addAction
98
- }
61
+ const newActions = ({onChange, inputId, active}: PatchStuff & {active?: boolean}) =>
62
+ active
63
+ ? defineDocumentFieldAction({
64
+ name: 'Experiment',
65
+ useAction: (props) => useRemoveExperimentAction({...props, onChange, inputId}),
66
+ })
67
+ : defineDocumentFieldAction({
68
+ name: 'Experiment',
69
+ useAction: (props) => useAddExperimentAction({...props, onChange, inputId}),
70
+ })
99
71
 
100
- export const ExperimentField = (
101
- props: ObjectFieldProps & {experimentNameOverride: string; experimentId: string},
102
- ) => {
72
+ export const ExperimentField = (props: ObjectFieldProps) => {
103
73
  const {onChange} = props.inputProps
104
- const {inputId, experimentNameOverride, experimentId} = props
74
+ const {inputId} = props
105
75
  const active = props.value?.active as boolean | undefined
106
76
 
107
77
  const oldActions = props.actions || []
108
78
 
109
79
  const withActionProps = {
110
80
  ...props,
111
- actions: [
112
- newActions({onChange, inputId, active, experimentNameOverride, experimentId}),
113
- ...oldActions,
114
- ],
81
+ actions: [newActions({onChange, inputId, active}), ...oldActions],
115
82
  }
116
83
  return props.renderDefault(withActionProps)
117
84
  }
@@ -20,14 +20,11 @@ const formatlistOptions = (experiments: ExperimentType[]): SelectOption[] =>
20
20
  value: experiment.id,
21
21
  }))
22
22
 
23
- export const ExperimentInput = (props: StringInputProps & {variantNameOverride: string}) => {
23
+ export const ExperimentInput = (props: StringInputProps) => {
24
24
  const {experiments} = useExperimentContext()
25
25
 
26
26
  const id = useFormValue(['_id']) as string
27
- const aditionalChangePath = useMemo(
28
- () => [...props.path.slice(0, -1), props.variantNameOverride],
29
- [props.variantNameOverride, props.path],
30
- )
27
+ const aditionalChangePath = useMemo(() => [...props.path.slice(0, -1), 'variants'], [props.path])
31
28
  const subValues = useFormValue(aditionalChangePath)
32
29
 
33
30
  const {patch} = useDocumentOperation(id.replace('drafts.', ''), props.schemaType.name)
@@ -0,0 +1,47 @@
1
+ import {SettingsView, useSecrets} from '@sanity/studio-secrets'
2
+ import {useEffect, useState} from 'react'
3
+ import {ObjectInputProps} from 'sanity'
4
+
5
+ import {useExperimentContext} from './ExperimentContext'
6
+
7
+ const namespace = 'growthbook'
8
+
9
+ const pluginConfigKeys = [
10
+ {
11
+ key: 'apiKey',
12
+ title: 'Your secret API key',
13
+ },
14
+ ]
15
+
16
+ export const Secrets = (props: ObjectInputProps) => {
17
+ const {secrets, loading} = useSecrets(namespace) as {secrets: {apiKey: string}; loading: boolean}
18
+ const {setSecret} = useExperimentContext()
19
+ const [showSettings, setShowSettings] = useState<boolean>(false)
20
+
21
+ useEffect(() => {
22
+ if (loading) return undefined
23
+ if (!secrets && !loading) {
24
+ setSecret(undefined)
25
+ return setShowSettings(true)
26
+ }
27
+ setSecret(secrets.apiKey)
28
+ return setShowSettings(false)
29
+ }, [secrets, loading, setSecret])
30
+
31
+ if (!showSettings) {
32
+ return props.renderDefault(props)
33
+ }
34
+ return (
35
+ <>
36
+ <SettingsView
37
+ title={'Growthbook secret'}
38
+ namespace={namespace}
39
+ keys={pluginConfigKeys}
40
+ onClose={() => {
41
+ setShowSettings(false)
42
+ }}
43
+ />
44
+ {props.renderDefault(props)}
45
+ </>
46
+ )
47
+ }
@@ -5,6 +5,7 @@ import {
5
5
  defineType,
6
6
  FieldDefinition,
7
7
  isObjectInputProps,
8
+ SanityClient,
8
9
  } from 'sanity'
9
10
 
10
11
  import {ArrayInput} from './components/Array'
@@ -12,39 +13,26 @@ import {CONFIG_DEFAULT, ExperimentProvider} from './components/ExperimentContext
12
13
  import {ExperimentField} from './components/ExperimentField'
13
14
  import {ExperimentInput} from './components/ExperimentInput'
14
15
  import {VariantPreview} from './components/VariantPreview'
15
- import {FieldPluginConfig} from './types'
16
+ import {ExperimentType, FieldPluginConfig} from './types'
16
17
  import {flattenSchemaType} from './utils/flattenSchemaType'
17
18
 
18
- const createExperimentType = ({
19
+ const createFieldType = ({
19
20
  field,
20
- experimentNameOverride,
21
- variantNameOverride,
22
- variantId,
23
- variantArrayName,
24
- experimentId,
25
21
  }: {
26
22
  field: string | FieldDefinition
27
- experimentNameOverride: string
28
- variantNameOverride: string
29
- variantId: string
30
- variantArrayName: string
31
- experimentId: string
23
+ experiments:
24
+ | ExperimentType[]
25
+ | ((client: SanityClient, secret?: string) => Promise<ExperimentType[]>)
32
26
  }) => {
33
27
  const typeName = typeof field === `string` ? field : field.name
34
28
  const usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1)
35
- const variantName = `${variantNameOverride}${usedName}`
29
+ const objectName = `variant${usedName}`
36
30
 
37
31
  return defineType({
38
- name: `${experimentNameOverride}${usedName}`,
32
+ name: `experiment${usedName}`,
39
33
  type: 'object',
40
34
  components: {
41
- field: (props) => (
42
- <ExperimentField
43
- {...props}
44
- experimentId={experimentId}
45
- experimentNameOverride={experimentNameOverride}
46
- />
47
- ),
35
+ field: ExperimentField,
48
36
  },
49
37
  fields: [
50
38
  typeof field === `string`
@@ -64,37 +52,31 @@ const createExperimentType = ({
64
52
  hidden: true,
65
53
  }),
66
54
  defineField({
67
- name: experimentId,
55
+ title: 'Experiment',
56
+ name: 'experimentId',
68
57
  type: 'string',
69
58
  components: {
70
- input: (props) => (
71
- <ExperimentInput {...props} variantNameOverride={variantNameOverride} />
72
- ),
59
+ input: ExperimentInput,
73
60
  },
74
61
  hidden: ({parent}) => {
75
62
  return !parent?.active
76
63
  },
77
64
  }),
78
65
  defineField({
79
- name: variantArrayName,
66
+ name: 'variants',
80
67
  type: 'array',
81
68
  hidden: ({parent}) => {
82
- return !parent?.[experimentId]
69
+ return !parent?.experimentId
83
70
  },
84
71
  components: {
85
72
  input: (props: ArrayOfObjectsInputProps) => (
86
- <ArrayInput
87
- {...props}
88
- variantName={variantName}
89
- variantId={variantId}
90
- experimentId={experimentId}
91
- />
73
+ <ArrayInput {...props} objectName={objectName} />
92
74
  ),
93
75
  },
94
76
  of: [
95
77
  defineField({
96
- name: variantName,
97
- type: variantName,
78
+ name: objectName,
79
+ type: objectName,
98
80
  }),
99
81
  ],
100
82
  }),
@@ -102,22 +84,17 @@ const createExperimentType = ({
102
84
  })
103
85
  }
104
86
 
105
- const createVariantType = ({
87
+ const createFieldObjectType = ({
106
88
  field,
107
- variantNameOverride,
108
- variantId,
109
- experimentId,
110
89
  }: {
111
90
  field: string | FieldDefinition
112
- variantNameOverride: string
113
- variantId: string
114
- experimentId: string
91
+ experiments: ExperimentType[] | ((client: SanityClient) => Promise<ExperimentType[]>)
115
92
  }) => {
116
93
  const typeName = typeof field === `string` ? field : field.name
117
94
  const usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1)
118
95
  return defineType({
119
- name: `${variantNameOverride}${usedName}`,
120
- title: `${variantNameOverride} array ${usedName}`,
96
+ name: `variant${usedName}`,
97
+ title: `Experiment array ${usedName}`,
121
98
  type: 'object',
122
99
  components: {
123
100
  preview: VariantPreview,
@@ -125,12 +102,12 @@ const createVariantType = ({
125
102
  fields: [
126
103
  {
127
104
  type: 'string',
128
- name: variantId,
105
+ name: 'variantId',
129
106
  readOnly: true,
130
107
  },
131
108
  {
132
109
  type: 'string',
133
- name: experimentId,
110
+ name: 'experimentId',
134
111
  hidden: true,
135
112
  },
136
113
  typeof field === `string`
@@ -138,66 +115,36 @@ const createVariantType = ({
138
115
  defineField({
139
116
  name: 'value',
140
117
  type: field,
141
- // hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
118
+ hidden: ({parent}) => !parent?.variantId,
142
119
  })
143
120
  : // Pass in the configured options, but overwrite the name
144
121
  {
145
122
  ...field,
146
123
  name: 'value',
147
- // hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
124
+ hidden: ({parent}) => !parent?.variantId,
148
125
  },
149
126
  ],
150
127
  preview: {
151
128
  select: {
152
- variant: variantId,
153
- experiment: experimentId,
129
+ variant: 'variantId',
130
+ experiment: 'experimentId',
154
131
  value: 'value',
155
132
  },
156
133
  },
157
134
  })
158
135
  }
159
136
 
160
- const fieldSchema = ({
161
- fields,
162
- experimentNameOverride,
163
- variantNameOverride,
164
- variantId,
165
- variantArrayName,
166
- experimentId,
167
- }: Required<Omit<FieldPluginConfig, 'apiVersion' | 'experiments'>>) => {
137
+ const fieldSchema = ({fields, experiments}: FieldPluginConfig) => {
168
138
  return [
169
- ...fields.map((field) =>
170
- createVariantType({field, variantNameOverride, variantId, experimentId}),
171
- ),
172
- ...fields.map((field) =>
173
- createExperimentType({
174
- field,
175
- experimentNameOverride,
176
- variantNameOverride,
177
- variantId,
178
- variantArrayName,
179
- experimentId,
180
- }),
181
- ),
139
+ ...fields.map((field) => createFieldObjectType({field, experiments})),
140
+ ...fields.map((field) => createFieldType({field, experiments})),
182
141
  ]
183
142
  }
184
143
 
185
144
  export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) => {
186
145
  const pluginConfig = {...CONFIG_DEFAULT, ...config}
187
- const {fields, experimentNameOverride, variantNameOverride} = pluginConfig
188
-
189
- const experimentId = `${experimentNameOverride}Id`
190
- const variantArrayName = `${variantNameOverride}s`
191
- const variantId = `${variantNameOverride}Id`
192
-
193
- const fieldSchemaConfig = fieldSchema({
194
- fields,
195
- experimentNameOverride,
196
- variantNameOverride,
197
- variantId,
198
- variantArrayName,
199
- experimentId,
200
- })
146
+ const {fields, experiments} = pluginConfig
147
+ const fieldSchemaConfig = fieldSchema({fields, experiments})
201
148
  return {
202
149
  name: 'sanity-personalistaion-plugin-field-level-experiments',
203
150
  schema: {
@@ -215,22 +162,12 @@ export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) =>
215
162
  const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(
216
163
  (field) => field.type.name,
217
164
  )
218
- const hasExperiment = flatFieldTypeNames.some((name) =>
219
- name.startsWith(experimentNameOverride),
220
- )
165
+ const hasExperiment = flatFieldTypeNames.some((name) => name.startsWith('experiment'))
221
166
 
222
167
  if (!hasExperiment) {
223
168
  return props.renderDefault(props)
224
169
  }
225
- const providerProps = {
226
- ...props,
227
- experimentFieldPluginConfig: {
228
- ...pluginConfig,
229
- variantId,
230
- variantArrayName,
231
- experimentId,
232
- },
233
- }
170
+ const providerProps = {...props, experimentFieldPluginConfig: pluginConfig}
234
171
  return ExperimentProvider(providerProps)
235
172
  },
236
173
  },
@@ -0,0 +1,51 @@
1
+ import {definePlugin, FieldDefinition, isObjectInputProps} from 'sanity'
2
+
3
+ import {Secrets} from './components/Secrets'
4
+ import {fieldLevelExperiments} from './fieldExperiments'
5
+ import {flattenSchemaType} from './utils/flattenSchemaType'
6
+ import {getExperiments} from './utils/growthbook'
7
+
8
+ export type GrowthbookABConfig = {
9
+ fields: (string | FieldDefinition)[]
10
+ environment: string
11
+ baseUrl?: string
12
+ project?: string
13
+ convertBooleans?: boolean
14
+ }
15
+
16
+ export const growthbookFieldLevel = definePlugin<GrowthbookABConfig>((config) => {
17
+ const {fields, environment, project, convertBooleans, baseUrl} = config
18
+ return {
19
+ name: 'sanity-growthbook-personalistaion-plugin-field-level-experiments',
20
+ plugins: [
21
+ fieldLevelExperiments({
22
+ fields,
23
+ experiments: (client) =>
24
+ getExperiments({client, environment, baseUrl, project, convertBooleans}),
25
+ }),
26
+ ],
27
+
28
+ form: {
29
+ components: {
30
+ input: (props) => {
31
+ const isRootInput = props.id === 'root' && isObjectInputProps(props)
32
+
33
+ if (!isRootInput) {
34
+ return props.renderDefault(props)
35
+ }
36
+
37
+ const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(
38
+ (field) => field.type.name,
39
+ )
40
+
41
+ const hasExperiment = flatFieldTypeNames.some((name) => name.startsWith('experiment'))
42
+
43
+ if (!hasExperiment) {
44
+ return props.renderDefault(props)
45
+ }
46
+ return Secrets(props)
47
+ },
48
+ },
49
+ },
50
+ }
51
+ })
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './fieldExperiments'
2
+ export * from './growthbookFieldExperiments'
2
3
  export * from './types'
3
4
  export * from './utils/flattenSchemaType'