@sanity/personalization-plugin 2.3.0-launch-darkly.2 → 2.4.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/README.md +2 -0
- package/dist/growthbook/index.d.mts +15 -0
- package/dist/growthbook/index.d.ts +15 -0
- package/dist/growthbook/index.js +120 -0
- package/dist/growthbook/index.js.map +1 -0
- package/dist/growthbook/index.mjs +124 -0
- package/dist/growthbook/index.mjs.map +1 -0
- package/dist/index.d.mts +188 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.js +15 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +16 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/components/ExperimentField.tsx +1 -5
- package/src/components/ExperimentInput.tsx +6 -5
- package/src/fieldExperiments.tsx +6 -5
- package/src/growthbook/Components/GrowthbookContext.tsx +38 -0
- package/src/{launchDarkly/components → growthbook/Components}/Secrets.tsx +6 -5
- package/src/growthbook/index.ts +54 -0
- package/src/growthbook/types.ts +15 -0
- package/src/growthbook/utils.ts +94 -0
- package/src/types.ts +176 -0
- package/src/utils/flattenSchemaType.ts +11 -14
- package/dist/launchDarkly/index.d.mts +0 -12
- package/dist/launchDarkly/index.d.ts +0 -12
- package/dist/launchDarkly/index.js +0 -103
- package/dist/launchDarkly/index.js.map +0 -1
- package/dist/launchDarkly/index.mjs +0 -107
- package/dist/launchDarkly/index.mjs.map +0 -1
- package/src/launchDarkly/components/LaunchDarklyContext.tsx +0 -36
- package/src/launchDarkly/index.ts +0 -52
- package/src/launchDarkly/types.ts +0 -193
- package/src/launchDarkly/utils.ts +0 -54
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/personalization-plugin",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Plugin to help with personalization, a/b testing when using Sanity",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"import": "./dist/index.mjs",
|
|
25
25
|
"default": "./dist/index.js"
|
|
26
26
|
},
|
|
27
|
-
"./
|
|
28
|
-
"source": "./src/
|
|
29
|
-
"import": "./dist/
|
|
30
|
-
"default": "./dist/
|
|
27
|
+
"./growthbook": {
|
|
28
|
+
"source": "./src/growthbook/index.ts",
|
|
29
|
+
"import": "./dist/growthbook/index.mjs",
|
|
30
|
+
"default": "./dist/growthbook/index.js"
|
|
31
31
|
},
|
|
32
32
|
"./package.json": "./package.json"
|
|
33
33
|
},
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
53
|
-
"@sanity/studio-secrets": "^3.0.
|
|
53
|
+
"@sanity/studio-secrets": "^3.0.0",
|
|
54
54
|
"@sanity/ui": "^2.8.19",
|
|
55
55
|
"@sanity/uuid": "^3.0.2",
|
|
56
56
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -42,7 +42,6 @@ const useRemoveExperimentAction = (
|
|
|
42
42
|
},
|
|
43
43
|
): DocumentFieldActionItem => {
|
|
44
44
|
const {onChange, active, experimentId, experimentNameOverride, variantNameOverride} = props
|
|
45
|
-
|
|
46
45
|
const handleClearAction = useCallback(() => {
|
|
47
46
|
const activeId = ['active']
|
|
48
47
|
const experiment = [experimentId]
|
|
@@ -97,10 +96,7 @@ const createActions = ({
|
|
|
97
96
|
experimentId,
|
|
98
97
|
}),
|
|
99
98
|
})
|
|
100
|
-
|
|
101
|
-
return removeAction
|
|
102
|
-
}
|
|
103
|
-
return addAction
|
|
99
|
+
return active ? removeAction : addAction
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
export const ExperimentField = (
|
|
@@ -2,6 +2,7 @@ import {Card, Text} from '@sanity/ui'
|
|
|
2
2
|
import {FormEvent, useCallback, useMemo} from 'react'
|
|
3
3
|
import {
|
|
4
4
|
FormPatch,
|
|
5
|
+
getPublishedId,
|
|
5
6
|
PatchEvent,
|
|
6
7
|
set,
|
|
7
8
|
StringInputProps,
|
|
@@ -27,13 +28,13 @@ export const ExperimentInput = (
|
|
|
27
28
|
const {experiments} = useExperimentContext()
|
|
28
29
|
|
|
29
30
|
const id = useFormValue(['_id']) as string
|
|
30
|
-
const
|
|
31
|
+
const additionalChangePath = useMemo(
|
|
31
32
|
() => [...props.path.slice(0, -1), `${props.variantNameOverride}s`],
|
|
32
33
|
[props.variantNameOverride, props.path],
|
|
33
34
|
)
|
|
35
|
+
const subValues = useFormValue(additionalChangePath)
|
|
34
36
|
|
|
35
|
-
const
|
|
36
|
-
const {patch} = useDocumentOperation(id.replace('drafts.', ''), props.schemaType.name)
|
|
37
|
+
const {patch} = useDocumentOperation(getPublishedId(id), props.schemaType.name)
|
|
37
38
|
|
|
38
39
|
const handleChange = useCallback(
|
|
39
40
|
(
|
|
@@ -51,12 +52,12 @@ export const ExperimentInput = (
|
|
|
51
52
|
|
|
52
53
|
if (subValues) {
|
|
53
54
|
const patchEvent = {
|
|
54
|
-
unset: [
|
|
55
|
+
unset: [additionalChangePath.join('.')],
|
|
55
56
|
}
|
|
56
57
|
patch.execute([patchEvent])
|
|
57
58
|
}
|
|
58
59
|
},
|
|
59
|
-
[patch, subValues,
|
|
60
|
+
[patch, subValues, additionalChangePath],
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
if (!experiments.length)
|
package/src/fieldExperiments.tsx
CHANGED
|
@@ -219,16 +219,17 @@ export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) =>
|
|
|
219
219
|
return props.renderDefault(props)
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
const flatFields = flattenSchemaType(props.schemaType)
|
|
223
|
+
const hasExperiment = flatFields.some(
|
|
224
|
+
(field) =>
|
|
225
|
+
field.type.name.startsWith(experimentNameOverride) ||
|
|
226
|
+
field.name.startsWith(experimentNameOverride),
|
|
227
227
|
)
|
|
228
228
|
|
|
229
229
|
if (!hasExperiment) {
|
|
230
230
|
return props.renderDefault(props)
|
|
231
231
|
}
|
|
232
|
+
|
|
232
233
|
const providerProps = {
|
|
233
234
|
...props,
|
|
234
235
|
experimentFieldPluginConfig: {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {createContext, useContext, useMemo, useState} from 'react'
|
|
2
|
+
import {ObjectInputProps} from 'sanity'
|
|
3
|
+
|
|
4
|
+
import {GrowthbookContextProps, GrowthbookExperimentFieldPluginConfig} from '../types'
|
|
5
|
+
import {Secrets} from './Secrets'
|
|
6
|
+
|
|
7
|
+
export const GROWTHBOOK_CONFIG_DEFAULT = {
|
|
8
|
+
baseUrl: 'https://api.growthbook.io/api/v1',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const GrowthbookContext = createContext<GrowthbookContextProps>({
|
|
12
|
+
setSecret: () => undefined,
|
|
13
|
+
secret: undefined,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export function useGrowthbookContext() {
|
|
17
|
+
return useContext(GrowthbookContext)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type GrowthbookProps = ObjectInputProps & {
|
|
21
|
+
growthbookFieldPluginConfig: GrowthbookExperimentFieldPluginConfig
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function GrowthbookProvider(props: GrowthbookProps) {
|
|
25
|
+
const {growthbookFieldPluginConfig} = props
|
|
26
|
+
const [secret, setSecret] = useState<string | undefined>()
|
|
27
|
+
|
|
28
|
+
const context = useMemo(
|
|
29
|
+
() => ({...growthbookFieldPluginConfig, secret, setSecret}),
|
|
30
|
+
[growthbookFieldPluginConfig, secret, setSecret],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<GrowthbookContext.Provider value={context}>
|
|
35
|
+
<Secrets {...props} />
|
|
36
|
+
</GrowthbookContext.Provider>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -2,10 +2,11 @@ import {SettingsView, useSecrets} from '@sanity/studio-secrets'
|
|
|
2
2
|
import {useEffect, useState} from 'react'
|
|
3
3
|
import {ObjectInputProps} from 'sanity'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {useGrowthbookContext} from './GrowthbookContext'
|
|
6
6
|
|
|
7
|
-
const namespace = '
|
|
8
|
-
|
|
7
|
+
export const namespace = 'growthbook'
|
|
8
|
+
|
|
9
|
+
export const pluginConfigKeys = [
|
|
9
10
|
{
|
|
10
11
|
key: 'apiKey',
|
|
11
12
|
title: 'Your secret API key',
|
|
@@ -14,7 +15,7 @@ const pluginConfigKeys = [
|
|
|
14
15
|
|
|
15
16
|
export const Secrets = (props: ObjectInputProps) => {
|
|
16
17
|
const {secrets, loading} = useSecrets(namespace) as {secrets: {apiKey: string}; loading: boolean}
|
|
17
|
-
const {setSecret} =
|
|
18
|
+
const {setSecret} = useGrowthbookContext()
|
|
18
19
|
const [showSettings, setShowSettings] = useState<boolean>(false)
|
|
19
20
|
|
|
20
21
|
useEffect(() => {
|
|
@@ -33,7 +34,7 @@ export const Secrets = (props: ObjectInputProps) => {
|
|
|
33
34
|
return (
|
|
34
35
|
<>
|
|
35
36
|
<SettingsView
|
|
36
|
-
title={
|
|
37
|
+
title={'Growthbook secret'}
|
|
37
38
|
namespace={namespace}
|
|
38
39
|
keys={pluginConfigKeys}
|
|
39
40
|
onClose={() => {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {definePlugin, isObjectInputProps} from 'sanity'
|
|
2
|
+
|
|
3
|
+
import {fieldLevelExperiments as baseFieldLevelExperiments} from '../fieldExperiments'
|
|
4
|
+
import {flattenSchemaType} from '../utils/flattenSchemaType'
|
|
5
|
+
import {GROWTHBOOK_CONFIG_DEFAULT, GrowthbookProvider} from './Components/GrowthbookContext'
|
|
6
|
+
import {GrowthbookExperimentFieldPluginConfig} from './types'
|
|
7
|
+
import {getExperiments} from './utils'
|
|
8
|
+
|
|
9
|
+
export const fieldLevelExperiments = definePlugin<GrowthbookExperimentFieldPluginConfig>(
|
|
10
|
+
(config) => {
|
|
11
|
+
const pluginConfig = {...GROWTHBOOK_CONFIG_DEFAULT, ...config}
|
|
12
|
+
const {fields, environment, project, convertBooleans, baseUrl, tags} = pluginConfig
|
|
13
|
+
return {
|
|
14
|
+
name: 'sanity-growthbook-personalistaion-plugin-field-level-experiments',
|
|
15
|
+
plugins: [
|
|
16
|
+
baseFieldLevelExperiments({
|
|
17
|
+
fields,
|
|
18
|
+
experiments: (client) =>
|
|
19
|
+
getExperiments({client, environment, baseUrl, project, convertBooleans, tags}),
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
form: {
|
|
24
|
+
components: {
|
|
25
|
+
input: (props) => {
|
|
26
|
+
const isRootInput = props.id === 'root' && isObjectInputProps(props)
|
|
27
|
+
|
|
28
|
+
if (!isRootInput) {
|
|
29
|
+
return props.renderDefault(props)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(
|
|
33
|
+
(field) => field.type.name,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const hasExperiment = flatFieldTypeNames.some((name) => name.startsWith('experiment'))
|
|
37
|
+
|
|
38
|
+
if (!hasExperiment) {
|
|
39
|
+
return props.renderDefault(props)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const providerProps = {
|
|
43
|
+
...props,
|
|
44
|
+
growthbookFieldPluginConfig: {
|
|
45
|
+
...pluginConfig,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
return GrowthbookProvider(providerProps)
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {FieldDefinition} from 'sanity'
|
|
2
|
+
|
|
3
|
+
export type GrowthbookExperimentFieldPluginConfig = {
|
|
4
|
+
fields: (string | FieldDefinition)[]
|
|
5
|
+
environment: string
|
|
6
|
+
baseUrl?: string
|
|
7
|
+
project?: string
|
|
8
|
+
convertBooleans?: boolean
|
|
9
|
+
tags?: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type GrowthbookContextProps = {
|
|
13
|
+
setSecret: (secret: string | undefined) => void
|
|
14
|
+
secret: string | undefined
|
|
15
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {SanityClient} from 'sanity'
|
|
2
|
+
|
|
3
|
+
import {ExperimentType, GrowthbookFeature, VariantType} from '../types'
|
|
4
|
+
import {namespace, pluginConfigKeys} from './Components/Secrets'
|
|
5
|
+
import {GrowthbookExperimentFieldPluginConfig} from './types'
|
|
6
|
+
|
|
7
|
+
const getBooleanConversion = (value: string) => {
|
|
8
|
+
// control is false
|
|
9
|
+
if (value === 'true') {
|
|
10
|
+
return 'variant'
|
|
11
|
+
} else if (value === 'false') {
|
|
12
|
+
return 'control'
|
|
13
|
+
}
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const getExperiments = async ({
|
|
18
|
+
client,
|
|
19
|
+
environment,
|
|
20
|
+
baseUrl,
|
|
21
|
+
project,
|
|
22
|
+
convertBooleans,
|
|
23
|
+
tags,
|
|
24
|
+
}: Omit<GrowthbookExperimentFieldPluginConfig, 'fields' | 'baseUrl'> & {
|
|
25
|
+
client: SanityClient
|
|
26
|
+
baseUrl: string
|
|
27
|
+
}): Promise<ExperimentType[]> => {
|
|
28
|
+
const query = `*[_id == 'secrets.${namespace}'][0].secrets.${pluginConfigKeys[0].key}`
|
|
29
|
+
|
|
30
|
+
const secret = await client.fetch(query) // secret is stored in the content lake using @sanity/studio-secrets
|
|
31
|
+
if (!secret) return []
|
|
32
|
+
|
|
33
|
+
const featureExperiments: ExperimentType[] = []
|
|
34
|
+
let hasMore = true
|
|
35
|
+
let offset = 0
|
|
36
|
+
const url = new URL(`${baseUrl}/features`)
|
|
37
|
+
if (project) {
|
|
38
|
+
url.searchParams.set('projectId', project)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
while (hasMore) {
|
|
42
|
+
url.searchParams.set('offset', offset.toString())
|
|
43
|
+
const response = await fetch(url, {
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: `Bearer ${secret}`,
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const {features, hasMore: responseHasMore, nextOffset} = await response.json()
|
|
50
|
+
|
|
51
|
+
hasMore = responseHasMore
|
|
52
|
+
offset = nextOffset
|
|
53
|
+
if (!features) continue
|
|
54
|
+
|
|
55
|
+
features.forEach((feature: GrowthbookFeature) => {
|
|
56
|
+
if (feature.archived) {
|
|
57
|
+
return undefined
|
|
58
|
+
}
|
|
59
|
+
if (tags && feature.tags && !feature.tags.some((tag) => tags.includes(tag))) {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const experiments = feature.environments[environment]?.rules.filter(
|
|
64
|
+
(experiment) => experiment.type === 'experiment-ref' || experiment.type === 'experiment',
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if (!experiments) {
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const variations: VariantType[] = []
|
|
72
|
+
const uniqueValues = new Set<string>()
|
|
73
|
+
|
|
74
|
+
experiments.forEach((experiment) => {
|
|
75
|
+
experiment?.variations.forEach((variant) => {
|
|
76
|
+
const value = convertBooleans ? getBooleanConversion(variant.value) : variant.value
|
|
77
|
+
if (!uniqueValues.has(value)) {
|
|
78
|
+
uniqueValues.add(value)
|
|
79
|
+
variations.push({
|
|
80
|
+
id: value,
|
|
81
|
+
label: value,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
const value = {id: feature.id, label: feature.id, variants: variations}
|
|
87
|
+
|
|
88
|
+
featureExperiments.push(value)
|
|
89
|
+
return undefined
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
const sortedFeatureExperiments = featureExperiments.sort((a, b) => a.id.localeCompare(b.id))
|
|
93
|
+
return sortedFeatureExperiments
|
|
94
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -67,3 +67,179 @@ export type ExperimentGeneric<T> = {
|
|
|
67
67
|
| T
|
|
68
68
|
| undefined
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
export type GrowthbookExperiment = {
|
|
72
|
+
id: string
|
|
73
|
+
dateCreated: string
|
|
74
|
+
dateUpdated: string
|
|
75
|
+
name: string
|
|
76
|
+
project: string
|
|
77
|
+
hypothesis: string
|
|
78
|
+
description: string
|
|
79
|
+
tags: [string]
|
|
80
|
+
owner: string
|
|
81
|
+
archived: boolean
|
|
82
|
+
status: string
|
|
83
|
+
autoRefresh: boolean
|
|
84
|
+
hashAttribute: string
|
|
85
|
+
fallbackAttribute: string
|
|
86
|
+
hashVersion: number
|
|
87
|
+
disableStickyBucketing: boolean
|
|
88
|
+
bucketVersion: number
|
|
89
|
+
minBucketVersion: number
|
|
90
|
+
variations: [
|
|
91
|
+
{
|
|
92
|
+
variationId: string
|
|
93
|
+
key: string
|
|
94
|
+
name: string
|
|
95
|
+
description: string
|
|
96
|
+
screenshots: [string]
|
|
97
|
+
},
|
|
98
|
+
]
|
|
99
|
+
phases: [
|
|
100
|
+
{
|
|
101
|
+
name: string
|
|
102
|
+
dateStarted: string
|
|
103
|
+
dateEnded: string
|
|
104
|
+
reasonForStopping: string
|
|
105
|
+
seed: string
|
|
106
|
+
coverage: 0
|
|
107
|
+
trafficSplit: [
|
|
108
|
+
{
|
|
109
|
+
variationId: string
|
|
110
|
+
weight: 0
|
|
111
|
+
},
|
|
112
|
+
]
|
|
113
|
+
namespace: {
|
|
114
|
+
namespaceId: string
|
|
115
|
+
range: []
|
|
116
|
+
}
|
|
117
|
+
targetingCondition: string
|
|
118
|
+
savedGroupTargeting: [
|
|
119
|
+
{
|
|
120
|
+
matchType: string
|
|
121
|
+
savedGroups: [string]
|
|
122
|
+
},
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
settings: {
|
|
127
|
+
datasourceId: string
|
|
128
|
+
assignmentQueryId: string
|
|
129
|
+
experimentId: string
|
|
130
|
+
segmentId: string
|
|
131
|
+
queryFilter: string
|
|
132
|
+
inProgressConversions: string
|
|
133
|
+
attributionModel: string
|
|
134
|
+
statsEngine: string
|
|
135
|
+
regressionAdjustmentEnabled: boolean
|
|
136
|
+
goals: [
|
|
137
|
+
{
|
|
138
|
+
metricId: string
|
|
139
|
+
overrides: {
|
|
140
|
+
delayHours: 0
|
|
141
|
+
windowHours: 0
|
|
142
|
+
window: string
|
|
143
|
+
winRiskThreshold: 0
|
|
144
|
+
loseRiskThreshold: 0
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
]
|
|
148
|
+
secondaryMetrics: [
|
|
149
|
+
{
|
|
150
|
+
metricId: string
|
|
151
|
+
overrides: {
|
|
152
|
+
delayHours: 0
|
|
153
|
+
windowHours: 0
|
|
154
|
+
window: string
|
|
155
|
+
winRiskThreshold: 0
|
|
156
|
+
loseRiskThreshold: 0
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
]
|
|
160
|
+
guardrails: [
|
|
161
|
+
{
|
|
162
|
+
metricId: string
|
|
163
|
+
overrides: {
|
|
164
|
+
delayHours: 0
|
|
165
|
+
windowHours: 0
|
|
166
|
+
window: string
|
|
167
|
+
winRiskThreshold: 0
|
|
168
|
+
loseRiskThreshold: 0
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
]
|
|
172
|
+
activationMetric: {
|
|
173
|
+
metricId: string
|
|
174
|
+
overrides: {
|
|
175
|
+
delayHours: 0
|
|
176
|
+
windowHours: 0
|
|
177
|
+
window: string
|
|
178
|
+
winRiskThreshold: 0
|
|
179
|
+
loseRiskThreshold: 0
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
resultSummary: {
|
|
184
|
+
status: string
|
|
185
|
+
winner: string
|
|
186
|
+
conclusions: string
|
|
187
|
+
releasedVariationId: string
|
|
188
|
+
excludeFromPayload: boolean
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export type GrowthbookFeature = {
|
|
193
|
+
id: string
|
|
194
|
+
dateCreated: string
|
|
195
|
+
dateUpdated: string
|
|
196
|
+
archived: boolean
|
|
197
|
+
description: string
|
|
198
|
+
owner: string
|
|
199
|
+
project: string
|
|
200
|
+
valueType: string
|
|
201
|
+
defaultValue: string
|
|
202
|
+
tags: string[]
|
|
203
|
+
environments: {
|
|
204
|
+
[key: string]: {
|
|
205
|
+
enabled: boolean
|
|
206
|
+
defaultValue: string
|
|
207
|
+
rules: {
|
|
208
|
+
description: string
|
|
209
|
+
condition: string
|
|
210
|
+
savedGroupTargeting: {matchType: string; savedGroups: string[]}[]
|
|
211
|
+
id: string
|
|
212
|
+
enabled: boolean
|
|
213
|
+
type: string
|
|
214
|
+
value: string
|
|
215
|
+
variations: {value: string; variationId: string}[]
|
|
216
|
+
}[]
|
|
217
|
+
definition: string
|
|
218
|
+
draft: {
|
|
219
|
+
enabled: boolean
|
|
220
|
+
defaultValue: string
|
|
221
|
+
rules: {
|
|
222
|
+
description: string
|
|
223
|
+
condition: string
|
|
224
|
+
savedGroupTargeting: {matchType: string; savedGroups: string[]}[]
|
|
225
|
+
id: string
|
|
226
|
+
enabled: boolean
|
|
227
|
+
type: string
|
|
228
|
+
value: string
|
|
229
|
+
variations: {value: string; variationId: string}[]
|
|
230
|
+
}[]
|
|
231
|
+
definition: string
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
prerequisites: {
|
|
236
|
+
parentId: string
|
|
237
|
+
parentCondition: string
|
|
238
|
+
}[]
|
|
239
|
+
revision: {
|
|
240
|
+
version: number
|
|
241
|
+
comment: string
|
|
242
|
+
date: string
|
|
243
|
+
publishedBy: string
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -11,7 +11,7 @@ export function flattenSchemaType(schemaType: SchemaType): ObjectFieldWithPath[]
|
|
|
11
11
|
return []
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
return extractInnerFields(schemaType.fields, [],
|
|
14
|
+
return extractInnerFields(schemaType.fields, [], 5)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function extractInnerFields(
|
|
@@ -28,20 +28,17 @@ function extractInnerFields(
|
|
|
28
28
|
|
|
29
29
|
if (field.type.jsonType === 'object') {
|
|
30
30
|
const innerFields = extractInnerFields(field.type.fields, [...path, field.name], maxDepth)
|
|
31
|
-
|
|
32
31
|
return [...acc, thisFieldWithPath, ...innerFields]
|
|
33
|
-
} else if (
|
|
34
|
-
|
|
35
|
-
field.type.of
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
44
|
-
|
|
32
|
+
} else if (field.type.jsonType === 'array') {
|
|
33
|
+
// Handle array types by checking each possible type in the array
|
|
34
|
+
const arrayTypes = field.type.of || []
|
|
35
|
+
const innerFields = arrayTypes.reduce<ObjectFieldWithPath[]>((arrayAcc, arrayType) => {
|
|
36
|
+
if ('fields' in arrayType) {
|
|
37
|
+
const typeFields = extractInnerFields(arrayType.fields, [...path, field.name], maxDepth)
|
|
38
|
+
return [...arrayAcc, ...typeFields]
|
|
39
|
+
}
|
|
40
|
+
return arrayAcc
|
|
41
|
+
}, [])
|
|
45
42
|
return [...acc, thisFieldWithPath, ...innerFields]
|
|
46
43
|
}
|
|
47
44
|
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {FieldDefinition} from 'sanity'
|
|
2
|
-
import {Plugin as Plugin_2} from 'sanity'
|
|
3
|
-
|
|
4
|
-
export declare const fieldLevelExperiments: Plugin_2<LaunchDarklyFieldLevelConfig>
|
|
5
|
-
|
|
6
|
-
declare type LaunchDarklyFieldLevelConfig = {
|
|
7
|
-
fields: (string | FieldDefinition)[]
|
|
8
|
-
projectKey: string
|
|
9
|
-
tags?: string[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export {}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {FieldDefinition} from 'sanity'
|
|
2
|
-
import {Plugin as Plugin_2} from 'sanity'
|
|
3
|
-
|
|
4
|
-
export declare const fieldLevelExperiments: Plugin_2<LaunchDarklyFieldLevelConfig>
|
|
5
|
-
|
|
6
|
-
declare type LaunchDarklyFieldLevelConfig = {
|
|
7
|
-
fields: (string | FieldDefinition)[]
|
|
8
|
-
projectKey: string
|
|
9
|
-
tags?: string[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export {}
|