@sanity/personalization-plugin 2.5.0-launch-darkly.1 → 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.
Files changed (48) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +640 -109
  3. package/dist/growthbook/index.d.ts +12 -15
  4. package/dist/growthbook/index.d.ts.map +1 -0
  5. package/dist/growthbook/index.js +104 -68
  6. package/dist/growthbook/index.js.map +1 -1
  7. package/dist/index.d.ts +212 -252
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +417 -7195
  10. package/dist/index.js.map +1 -1
  11. package/dist/launchDarkly/index.d.ts +9 -12
  12. package/dist/launchDarkly/index.d.ts.map +1 -0
  13. package/dist/launchDarkly/index.js +74 -46
  14. package/dist/launchDarkly/index.js.map +1 -1
  15. package/package.json +36 -78
  16. package/dist/growthbook/index.d.mts +0 -15
  17. package/dist/growthbook/index.mjs +0 -124
  18. package/dist/growthbook/index.mjs.map +0 -1
  19. package/dist/index.d.mts +0 -267
  20. package/dist/index.mjs +0 -7347
  21. package/dist/index.mjs.map +0 -1
  22. package/dist/launchDarkly/index.d.mts +0 -12
  23. package/dist/launchDarkly/index.mjs +0 -107
  24. package/dist/launchDarkly/index.mjs.map +0 -1
  25. package/sanity.json +0 -8
  26. package/src/components/Array.tsx +0 -68
  27. package/src/components/ExperimentContext.tsx +0 -65
  28. package/src/components/ExperimentField.tsx +0 -138
  29. package/src/components/ExperimentInput.tsx +0 -75
  30. package/src/components/Select.tsx +0 -43
  31. package/src/components/VariantInput.tsx +0 -18
  32. package/src/components/VariantPreview.tsx +0 -75
  33. package/src/fieldExperiments.tsx +0 -264
  34. package/src/growthbook/Components/GrowthbookContext.tsx +0 -38
  35. package/src/growthbook/Components/Secrets.tsx +0 -47
  36. package/src/growthbook/index.ts +0 -54
  37. package/src/growthbook/types.ts +0 -15
  38. package/src/growthbook/utils.ts +0 -94
  39. package/src/index.ts +0 -3
  40. package/src/launchDarkly/components/LaunchDarklyContext.tsx +0 -36
  41. package/src/launchDarkly/components/Secrets.tsx +0 -46
  42. package/src/launchDarkly/index.ts +0 -52
  43. package/src/launchDarkly/launchdarkly.md +0 -76
  44. package/src/launchDarkly/types.ts +0 -193
  45. package/src/launchDarkly/utils.ts +0 -54
  46. package/src/types.ts +0 -245
  47. package/src/utils/flattenSchemaType.ts +0 -47
  48. package/v2-incompatible.js +0 -11
@@ -1,76 +0,0 @@
1
- # @sanity/personalization-plugin - launchDarklyFieldLevel
2
-
3
- ## Previously know as @sanity/personalisation-plugin
4
-
5
- > This is a **Sanity Studio v3** plugin.
6
-
7
- This plugin allows users to add a/b/n testing experiments to individual fields connecting to the [LaunchDarkly](https://launchdarkly.com//) A/B testing service.
8
-
9
- - [@sanity/personalization-plugin - launchDarklyFieldLevel](#sanitypersonalization-plugin---launchDarklyFieldLevel)
10
- - [Installation](#installation)
11
- - [Usage](#usage)
12
- - [Loading Experiments](#loading-experiments)
13
-
14
- This plugin is built on top of the `fieldLevelExperiments` export so see the main readme for details of:
15
-
16
- - [Using complex field configurations](/#using-complex-field-configurations)
17
- - [Validation of individual array items](/#validation-of-individual-array-items)
18
- - [Shape of stored data](/#shape-of-stored-data)
19
- - [Querying data](/#querying-data)
20
- - [License](#license)
21
- - [Develop \& test](#develop--test)
22
- - [Release new version](#release-new-version)
23
- - [License](#license-1)
24
-
25
- ## Installation
26
-
27
- ```sh
28
- npm install @sanity/personalization-plugin
29
- ```
30
-
31
- ## Usage
32
-
33
- Add it as a plugin in `sanity.config.ts` (or .js):
34
-
35
- ```ts
36
- import {defineConfig} from 'sanity'
37
- import {fieldLevelExperiments} from '@sanity/personalization-plugin/launchDarkly'
38
-
39
- export default defineConfig({
40
- //...
41
- plugins: [
42
- //...
43
- launchDarklyFieldLevel({
44
- fields: ['string'],
45
- projectKey: 'string', // required filter parameter for fetching features/variants
46
- tags: ['string'] //optional parameter that filters the list to flags that have all of the tags in the list
47
- }),
48
- ],
49
- })
50
- ```
51
-
52
- This will register two new fields to the schema., based on the setting passed into `fields:`
53
-
54
- - `flagString` an Object field with `string` field called `default`, a `string` field called `flagId` and an array field of type:
55
- - `variantsString` an object field with a `string` field called `value`, a string field called `variantId`, a `string` field called `flagId`.
56
-
57
- Use the flag field in your schema like this:
58
-
59
- ```ts
60
- //for Example in post.ts
61
-
62
- fields: [
63
- defineField({
64
- name: 'title',
65
- type: 'flagString',
66
- }),
67
- ]
68
- ```
69
-
70
- ## Loading Experiments
71
-
72
- This plugin uses [@sanity/studio-secrets](https://www.npmjs.com/package/@sanity/studio-secrets) for storing your Launch Darkly API key. The first time you open a document that has an experiment you will be asked to provide your API key. This is stored in a private document on the dataset.
73
-
74
- Once you have entered you API key the plugin will fetch feature flags and variants flags. If features/variants are updated on LaunchDarkly you will need to refresh the page.
75
-
76
- The values stored for an flag will be the Feature Key amd the variants will stored the variation value.
@@ -1,193 +0,0 @@
1
- import {FieldDefinition} from 'sanity'
2
-
3
- export type LaunchDarklyFieldLevelConfig = {
4
- fields: (string | FieldDefinition)[]
5
- projectKey: string
6
- tags?: string[]
7
- }
8
- export type LaunchDarklyContextProps = {
9
- setSecret: (secret: string | undefined) => void
10
- secret: string | undefined
11
- }
12
-
13
- export type LaunchDarklyFlagItem = {
14
- name: string
15
- kind: string
16
- key: string
17
- _version: number
18
- creationDate: number
19
- variations: Array<{
20
- value: boolean | string | number
21
- _id: string
22
- name: string
23
- }>
24
- temporary: boolean
25
- tags: string[]
26
- _links: {
27
- parent: {
28
- href: string
29
- type: string
30
- }
31
- self: {
32
- href: string
33
- type: string
34
- }
35
- }
36
- experiments: {
37
- baselineIdx: number
38
- items: Array<{
39
- metricKey: string
40
- _metric: {
41
- _id: string
42
- _versionId: string
43
- key: string
44
- name: string
45
- kind: string
46
- _links: {
47
- parent: {
48
- href: string
49
- type: string
50
- }
51
- self: {
52
- href: string
53
- type: string
54
- }
55
- }
56
- tags: string[]
57
- _creationDate: number
58
- experimentCount: number
59
- metricGroupCount: number
60
- _attachedFlagCount: number
61
- maintainerId: string
62
- _maintainer: {
63
- _links: {
64
- self: {
65
- href: string
66
- type: string
67
- }
68
- }
69
- _id: string
70
- role: string
71
- email: string
72
- firstName: string
73
- lastName: string
74
- }
75
- category: string
76
- isNumeric: boolean
77
- percentileValue: number
78
- }
79
- }>
80
- }
81
- customProperties: {
82
- key: {
83
- name: string
84
- value: string[]
85
- }
86
- }
87
- archived: boolean
88
- description: string
89
- maintainerId: string
90
- _maintainer: {
91
- _links: {
92
- self: {
93
- href: string
94
- type: string
95
- }
96
- }
97
- _id: string
98
- role: string
99
- email: string
100
- firstName: string
101
- lastName: string
102
- }
103
- maintainerTeamKey: string
104
- _maintainerTeam: {
105
- key: string
106
- name: string
107
- _links: {
108
- parent: {
109
- href: string
110
- type: string
111
- }
112
- roles: {
113
- href: string
114
- type: string
115
- }
116
- self: {
117
- href: string
118
- type: string
119
- }
120
- }
121
- }
122
- archivedDate: number
123
- deprecated: boolean
124
- deprecatedDate: number
125
- defaults: {
126
- onVariation: number
127
- offVariation: number
128
- }
129
- _purpose: string
130
- migrationSettings: {
131
- contextKind: string
132
- stageCount: number
133
- }
134
- environments: {
135
- [key: string]: {
136
- on: boolean
137
- archived: boolean
138
- salt: string
139
- sel: string
140
- lastModified: number
141
- version: number
142
- _site: {
143
- href: string
144
- type: string
145
- }
146
- _environmentName: string
147
- trackEvents: boolean
148
- trackEventsFallthrough: boolean
149
- targets: Array<{
150
- values: string[]
151
- variation: number
152
- contextKind: string
153
- }>
154
- contextTargets: Array<{
155
- values: string[]
156
- variation: number
157
- contextKind: string
158
- }>
159
- rules: Array<{
160
- clauses: Array<{
161
- attribute: string
162
- op: string
163
- values: unknown[]
164
- negate: boolean
165
- }>
166
- trackEvents: boolean
167
- }>
168
- fallthrough: {
169
- variation: number
170
- }
171
- offVariation: number
172
- prerequisites: Array<{
173
- key: string
174
- variation: number
175
- }>
176
- _summary: {
177
- variations: {
178
- [key: string]: {
179
- rules: number
180
- nullRules: number
181
- targets: number
182
- contextTargets: number
183
- isFallthrough?: boolean
184
- isOff?: boolean
185
- }
186
- }
187
- prerequisites: number
188
- }
189
- }
190
- }
191
- includeInSnippet: boolean
192
- goalIds: string[]
193
- }
@@ -1,54 +0,0 @@
1
- import {SanityClient} from 'sanity'
2
-
3
- import {ExperimentType} from '../types'
4
- import {LaunchDarklyFieldLevelConfig, LaunchDarklyFlagItem} from './types'
5
-
6
- export const getExperiments = async ({
7
- client,
8
- projectKey,
9
- tags,
10
- }: Omit<LaunchDarklyFieldLevelConfig, 'fields'> & {client: SanityClient}): Promise<
11
- ExperimentType[]
12
- > => {
13
- const query = `*[_id == 'secrets.launchdarkly'][0].secrets.apiKey`
14
-
15
- const secret = await client.fetch(query) // secret is stored in the content lake using @sanity/studio-secrets
16
- if (!secret) return []
17
-
18
- const url = new URL(`https://app.launchdarkly.com/api/v2/flags/${projectKey}`)
19
-
20
- if (tags) {
21
- url.searchParams.set('filter', `tags:${tags.join('+')}`)
22
- }
23
-
24
- const featureExperiments: ExperimentType[] = []
25
- let hasMore = true
26
- const offset = 0
27
- const limit = 10
28
-
29
- while (hasMore) {
30
- url.searchParams.set('offset', offset.toString())
31
- url.searchParams.set('limit', limit.toString())
32
- const responseFlags = await fetch(url, {
33
- headers: {
34
- Authorization: secret,
35
- },
36
- })
37
-
38
- const {items} = await responseFlags.json()
39
- const experiments = items.map((flag: LaunchDarklyFlagItem) => ({
40
- id: flag.key,
41
- label: flag.name,
42
- variants: flag.variations.map((variation) => ({
43
- id: variation.value.toString(),
44
- label: variation.name ?? variation.value.toString(),
45
- })),
46
- }))
47
- featureExperiments.push(...experiments)
48
- if (items.length !== limit) {
49
- hasMore = false
50
- }
51
- }
52
-
53
- return featureExperiments
54
- }
package/src/types.ts DELETED
@@ -1,245 +0,0 @@
1
- import {
2
- ArrayOfObjectsInputProps,
3
- FieldDefinition,
4
- ObjectField,
5
- Path,
6
- PreviewProps,
7
- SanityClient,
8
- SchemaType,
9
- } from 'sanity'
10
-
11
- export type VariantType = {
12
- id: string
13
- label: string
14
- }
15
- export type ExperimentType = {
16
- id: string
17
- label: string
18
- variants: VariantType[]
19
- }
20
-
21
- export type FieldPluginConfig = {
22
- fields: (string | FieldDefinition)[]
23
- experiments: ExperimentType[] | ((client: SanityClient) => Promise<ExperimentType[]>)
24
- apiVersion?: string
25
- experimentNameOverride?: string
26
- variantNameOverride?: string
27
- variantId?: string
28
- variantArrayName?: string
29
- experimentId?: string
30
- }
31
-
32
- export type VariantPreviewProps = Omit<PreviewProps, 'SchemaType'> & {
33
- [key: string]: string
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- value: any
36
- }
37
-
38
- export type ExperimentContextProps = Required<FieldPluginConfig> & {
39
- experiments: ExperimentType[]
40
- }
41
-
42
- export type ArrayInputProps = ArrayOfObjectsInputProps & {
43
- variantName: string
44
- variantId: string
45
- experimentId: string
46
- }
47
-
48
- export type ObjectFieldWithPath = ObjectField<SchemaType> & {path: Path}
49
-
50
- export type VariantGeneric<T> = {
51
- [key: string]: string | T | undefined
52
- _type: string
53
- value?: T
54
- }
55
-
56
- export type ExperimentGeneric<T> = {
57
- _type: string
58
- default?: T
59
- experimentValue?: string
60
- [key: string]:
61
- | Array<
62
- {
63
- _key: string
64
- } & VariantGeneric<T>
65
- >
66
- | string
67
- | T
68
- | undefined
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
- }
@@ -1,47 +0,0 @@
1
- import {isDocumentSchemaType, type ObjectField, type Path, type SchemaType} from 'sanity'
2
-
3
- import {ObjectFieldWithPath} from '../types'
4
-
5
- /**
6
- * Flattens a document's schema type into a flat array of fields and includes their path
7
- */
8
- export function flattenSchemaType(schemaType: SchemaType): ObjectFieldWithPath[] {
9
- if (!isDocumentSchemaType(schemaType)) {
10
- console.error(`Schema type is not a document`)
11
- return []
12
- }
13
-
14
- return extractInnerFields(schemaType.fields, [], 5)
15
- }
16
-
17
- function extractInnerFields(
18
- fields: ObjectField<SchemaType>[],
19
- path: Path,
20
- maxDepth: number,
21
- ): ObjectFieldWithPath[] {
22
- if (path.length >= maxDepth) {
23
- return []
24
- }
25
-
26
- return fields.reduce<ObjectFieldWithPath[]>((acc, field) => {
27
- const thisFieldWithPath = {path: [...path, field.name], ...field}
28
-
29
- if (field.type.jsonType === 'object') {
30
- const innerFields = extractInnerFields(field.type.fields, [...path, field.name], maxDepth)
31
- return [...acc, thisFieldWithPath, ...innerFields]
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
- }, [])
42
- return [...acc, thisFieldWithPath, ...innerFields]
43
- }
44
-
45
- return [...acc, thisFieldWithPath]
46
- }, [])
47
- }
@@ -1,11 +0,0 @@
1
- const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
- const {name, version, sanityExchangeUrl} = require('./package.json')
3
-
4
- export default showIncompatiblePluginDialog({
5
- name: name,
6
- versions: {
7
- v3: version,
8
- v2: undefined,
9
- },
10
- sanityExchangeUrl,
11
- })