@sanity/personalization-plugin 2.1.0-growthbook.1 → 2.2.0-launch-darkly.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/dist/index.mjs CHANGED
@@ -9,7 +9,12 @@ import { GiSoapExperiment } from "react-icons/gi";
9
9
  import { useSecrets, SettingsView } from "@sanity/studio-secrets";
10
10
  const CONFIG_DEFAULT = {
11
11
  fields: [],
12
- apiVersion: "2024-11-07"
12
+ apiVersion: "2024-11-07",
13
+ experimentNameOverride: "experiment",
14
+ variantNameOverride: "variant",
15
+ variantId: "variantId",
16
+ variantArrayName: "variants",
17
+ experimentId: "experimentId"
13
18
  }, ExperimentContext = createContext({
14
19
  ...CONFIG_DEFAULT,
15
20
  experiments: [],
@@ -23,7 +28,7 @@ function useExperimentContext() {
23
28
  function ExperimentProvider(props) {
24
29
  const { experimentFieldPluginConfig } = props, [secret, setSecret] = useState(), client = useClient({ apiVersion: experimentFieldPluginConfig.apiVersion }), workspace = useWorkspace(), experiments = Array.isArray(experimentFieldPluginConfig.experiments) ? experimentFieldPluginConfig.experiments : suspend(
25
30
  // eslint-disable-next-line require-await
26
- async () => typeof experimentFieldPluginConfig.experiments == "function" ? experimentFieldPluginConfig.experiments(client, secret) : experimentFieldPluginConfig.experiments,
31
+ async () => typeof experimentFieldPluginConfig.experiments == "function" ? experimentFieldPluginConfig.experiments(client) : experimentFieldPluginConfig.experiments,
27
32
  [workspace, secret],
28
33
  { equal }
29
34
  ), context = useMemo(
@@ -33,18 +38,18 @@ function ExperimentProvider(props) {
33
38
  return /* @__PURE__ */ jsx(ExperimentContext.Provider, { value: context, children: props.renderDefault(props) });
34
39
  }
35
40
  const ArrayInput = (props) => {
36
- const fieldPath = props.path.slice(0, -1), experimentId = useFormValue([...fieldPath, "experimentId"]), { experiments } = useExperimentContext(), { onItemAppend, objectName } = props, handleClick = useCallback(
41
+ const fieldPath = props.path.slice(0, -1), { onItemAppend, variantName, variantId, experimentId } = props, experimentValue = useFormValue([...fieldPath, experimentId]), { experiments } = useExperimentContext(), handleClick = useCallback(
37
42
  async (variant) => {
38
43
  const item = {
39
44
  _key: uuid(),
40
- variantId: variant.id,
41
- experimentId,
42
- _type: objectName
45
+ [variantId]: variant.id,
46
+ [experimentId]: experimentValue,
47
+ _type: variantName
43
48
  };
44
49
  onItemAppend(item);
45
50
  },
46
- [experimentId, objectName, onItemAppend]
47
- ), filteredVariants = experiments.find((option) => option.id === experimentId)?.variants || [], usedVariants = props.value?.map((variant) => variant.variantId);
51
+ [variantId, experimentId, experimentValue, variantName, onItemAppend]
52
+ ), filteredVariants = experiments.find((option) => option.id === experimentValue)?.variants || [], usedVariants = (props.value || [])?.map((variant) => variant[variantId]);
48
53
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
49
54
  props.renderDefault({ ...props, arrayFunctions: () => null }),
50
55
  /* @__PURE__ */ jsx(Inline, { space: 1, children: filteredVariants.map((variant) => /* @__PURE__ */ jsx(
@@ -55,7 +60,7 @@ const ArrayInput = (props) => {
55
60
  disabled: usedVariants?.includes(variant.id),
56
61
  onClick: () => handleClick(variant)
57
62
  },
58
- `${experimentId}-${variant.id}`
63
+ `${experimentValue}-${variant.id}`
59
64
  )) })
60
65
  ] });
61
66
  }, AccessDeniedIcon = forwardRef(function(props, ref) {
@@ -6405,40 +6410,60 @@ const icons = {
6405
6410
  });
6406
6411
  Icon.displayName = "ForwardRef(Icon)";
6407
6412
  const useAddExperimentAction = (props) => {
6408
- const patchActiveEvent = useMemo(() => set(!0, ["active"]), []), handleAction = useCallback(() => {
6409
- props.onChange([patchActiveEvent]);
6410
- }, [patchActiveEvent, props]);
6413
+ const { onChange, experimentNameOverride } = props, handleAddAction = () => {
6414
+ onChange([set(!0, ["active"])]);
6415
+ };
6411
6416
  return {
6412
- title: "Add experiment",
6417
+ title: `Add ${experimentNameOverride}`,
6413
6418
  type: "action",
6414
6419
  icon: GiSoapExperiment,
6415
- onAction: handleAction,
6420
+ onAction: handleAddAction,
6416
6421
  renderAsButton: !0
6417
6422
  };
6418
6423
  }, useRemoveExperimentAction = (props) => {
6419
- const patchActiveEvent = useMemo(() => set(!1, ["active"]), []), patchClearEvent = useMemo(() => {
6420
- const experimentId = ["experimentId"], variants = ["variants"];
6421
- return [unset(experimentId), unset(variants)];
6422
- }, []), handleAction = useCallback(() => {
6423
- props.onChange([patchActiveEvent, ...patchClearEvent]);
6424
- }, [patchActiveEvent, patchClearEvent, props]);
6424
+ const { onChange, experimentId, experimentNameOverride } = props, patchActiveFalseEvent = () => set(!1, ["active"]), patchClearEvent = () => {
6425
+ const experiment = [experimentId], variants = [experimentNameOverride];
6426
+ return [unset(experiment), unset(variants)];
6427
+ }, handleClearAction = () => {
6428
+ const clearEvents = patchClearEvent(), activeEvent = patchActiveFalseEvent();
6429
+ onChange([activeEvent, ...clearEvents]);
6430
+ };
6425
6431
  return {
6426
- title: "Remove experiment",
6432
+ title: `Remove ${experimentNameOverride}`,
6427
6433
  type: "action",
6428
6434
  icon: CloseIcon,
6429
- onAction: handleAction,
6435
+ onAction: handleClearAction,
6430
6436
  renderAsButton: !0
6431
6437
  };
6432
- }, newActions = ({ onChange, inputId, active }) => active ? defineDocumentFieldAction({
6433
- name: "Experiment",
6434
- useAction: (props) => useRemoveExperimentAction({ ...props, onChange, inputId })
6435
- }) : defineDocumentFieldAction({
6436
- name: "Experiment",
6437
- useAction: (props) => useAddExperimentAction({ ...props, onChange, inputId })
6438
- }), ExperimentField = (props) => {
6439
- const { onChange } = props.inputProps, { inputId } = props, active = props.value?.active, oldActions = props.actions || [], withActionProps = {
6438
+ }, newActions = ({
6439
+ onChange,
6440
+ inputId,
6441
+ active,
6442
+ experimentNameOverride,
6443
+ experimentId
6444
+ }) => {
6445
+ const removeAction = defineDocumentFieldAction({
6446
+ name: `Remove ${experimentNameOverride}`,
6447
+ useAction: (props) => useRemoveExperimentAction({
6448
+ onChange,
6449
+ experimentNameOverride,
6450
+ experimentId
6451
+ })
6452
+ }), addAction = defineDocumentFieldAction({
6453
+ name: `Add ${experimentNameOverride}`,
6454
+ useAction: (props) => useAddExperimentAction({
6455
+ onChange,
6456
+ experimentNameOverride
6457
+ })
6458
+ });
6459
+ return active ? removeAction : addAction;
6460
+ }, ExperimentField = (props) => {
6461
+ const { onChange } = props.inputProps, { inputId, experimentNameOverride, experimentId } = props, active = props.value?.active, oldActions = props.actions || [], withActionProps = {
6440
6462
  ...props,
6441
- actions: [newActions({ onChange, inputId, active }), ...oldActions]
6463
+ actions: [
6464
+ newActions({ onChange, inputId, active, experimentNameOverride, experimentId }),
6465
+ ...oldActions
6466
+ ]
6442
6467
  };
6443
6468
  return props.renderDefault(withActionProps);
6444
6469
  }, Select = (props) => {
@@ -6470,7 +6495,10 @@ const useAddExperimentAction = (props) => {
6470
6495
  title: experiment.label,
6471
6496
  value: experiment.id
6472
6497
  })), ExperimentInput = (props) => {
6473
- const { experiments } = useExperimentContext(), id = useFormValue(["_id"]), aditionalChangePath = useMemo(() => [...props.path.slice(0, -1), "variants"], [props.path]), subValues = useFormValue(aditionalChangePath), { patch } = useDocumentOperation(id.replace("drafts.", ""), props.schemaType.name), handleChange = useCallback(
6498
+ const { experiments } = useExperimentContext(), id = useFormValue(["_id"]), aditionalChangePath = useMemo(
6499
+ () => [...props.path.slice(0, -1), props.variantNameOverride],
6500
+ [props.variantNameOverride, props.path]
6501
+ ), subValues = useFormValue(aditionalChangePath), { patch } = useDocumentOperation(id.replace("drafts.", ""), props.schemaType.name), handleChange = useCallback(
6474
6502
  (event, onChange) => {
6475
6503
  const inputValue = event.currentTarget.value;
6476
6504
  if (onChange(inputValue ? set(inputValue) : unset()), subValues) {
@@ -6483,6 +6511,14 @@ const useAddExperimentAction = (props) => {
6483
6511
  [patch, subValues, aditionalChangePath]
6484
6512
  );
6485
6513
  return experiments.length ? /* @__PURE__ */ jsx(Select, { ...props, listOptions: formatlistOptions(experiments), handleChange }) : /* @__PURE__ */ jsx(Fragment, {});
6514
+ }, VariantInput = (props) => {
6515
+ const defaultValue = useFormValue([props.path[0], "default"]), handleClick = () => {
6516
+ props.onChange(set(defaultValue, ["value"]));
6517
+ };
6518
+ return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
6519
+ props.renderDefault(props),
6520
+ /* @__PURE__ */ jsx(Inline, { space: 1, children: /* @__PURE__ */ jsx(Button, { text: "Copy default", mode: "ghost", onClick: () => handleClick() }) })
6521
+ ] });
6486
6522
  }, VariantPreview = (props) => {
6487
6523
  const [subtitle, setSubtitle] = useState(void 0), [title, setTitle] = useState(void 0), [media, setMedia] = useState(void 0), client = useClient({ apiVersion: "2025-01-01" }), { experiments } = useExperimentContext(), { experiment, variant, value } = props, selectedExperiment = experiments.find((experimentItem) => experimentItem.id === experiment), selectedVariant = selectedExperiment?.variants.find((variantItem) => variantItem.id === variant);
6488
6524
  useEffect(() => {
@@ -6530,15 +6566,27 @@ function extractInnerFields(fields, path, maxDepth) {
6530
6566
  return [...acc, thisFieldWithPath];
6531
6567
  }, []);
6532
6568
  }
6533
- const createFieldType = ({
6534
- field
6569
+ const createExperimentType = ({
6570
+ field,
6571
+ experimentNameOverride,
6572
+ variantNameOverride,
6573
+ variantId,
6574
+ variantArrayName,
6575
+ experimentId
6535
6576
  }) => {
6536
- const typeName = typeof field == "string" ? field : field.name, usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1), objectName = `variant${usedName}`;
6577
+ const typeName = typeof field == "string" ? field : field.name, usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1), variantName = `${variantNameOverride}${usedName}`;
6537
6578
  return defineType({
6538
- name: `experiment${usedName}`,
6579
+ name: `${experimentNameOverride}${usedName}`,
6539
6580
  type: "object",
6540
6581
  components: {
6541
- field: ExperimentField
6582
+ field: (props) => /* @__PURE__ */ jsx(
6583
+ ExperimentField,
6584
+ {
6585
+ ...props,
6586
+ experimentId,
6587
+ experimentNameOverride
6588
+ }
6589
+ )
6542
6590
  },
6543
6591
  fields: [
6544
6592
  typeof field == "string" ? (
@@ -6560,105 +6608,151 @@ const createFieldType = ({
6560
6608
  hidden: !0
6561
6609
  }),
6562
6610
  defineField({
6563
- title: "Experiment",
6564
- name: "experimentId",
6611
+ name: experimentId,
6565
6612
  type: "string",
6566
6613
  components: {
6567
- input: ExperimentInput
6614
+ input: (props) => /* @__PURE__ */ jsx(ExperimentInput, { ...props, variantNameOverride })
6568
6615
  },
6569
6616
  hidden: ({ parent }) => !parent?.active
6570
6617
  }),
6571
6618
  defineField({
6572
- name: "variants",
6619
+ name: variantArrayName,
6573
6620
  type: "array",
6574
- hidden: ({ parent }) => !parent?.experimentId,
6621
+ hidden: ({ parent }) => !parent?.[experimentId],
6575
6622
  components: {
6576
- input: (props) => /* @__PURE__ */ jsx(ArrayInput, { ...props, objectName })
6623
+ input: (props) => /* @__PURE__ */ jsx(
6624
+ ArrayInput,
6625
+ {
6626
+ ...props,
6627
+ variantName,
6628
+ variantId,
6629
+ experimentId
6630
+ }
6631
+ )
6577
6632
  },
6578
6633
  of: [
6579
6634
  defineField({
6580
- name: objectName,
6581
- type: objectName
6635
+ name: variantName,
6636
+ type: variantName
6582
6637
  })
6583
6638
  ]
6584
6639
  })
6585
6640
  ]
6586
6641
  });
6587
- }, createFieldObjectType = ({
6588
- field
6642
+ }, createVariantType = ({
6643
+ field,
6644
+ variantNameOverride,
6645
+ variantId,
6646
+ experimentId
6589
6647
  }) => {
6590
6648
  const typeName = typeof field == "string" ? field : field.name, usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1);
6591
6649
  return defineType({
6592
- name: `variant${usedName}`,
6593
- title: `Experiment array ${usedName}`,
6650
+ name: `${variantNameOverride}${usedName}`,
6651
+ title: `${variantNameOverride} array ${usedName}`,
6594
6652
  type: "object",
6595
6653
  components: {
6596
- preview: VariantPreview
6654
+ preview: VariantPreview,
6655
+ input: VariantInput
6597
6656
  },
6598
6657
  fields: [
6599
6658
  {
6600
6659
  type: "string",
6601
- name: "variantId",
6660
+ name: variantId,
6602
6661
  readOnly: !0
6603
6662
  },
6604
6663
  {
6605
6664
  type: "string",
6606
- name: "experimentId",
6665
+ name: experimentId,
6607
6666
  hidden: !0
6608
6667
  },
6609
6668
  typeof field == "string" ? (
6610
6669
  // Define a simple field if all we have is the name as a string
6611
6670
  defineField({
6612
6671
  name: "value",
6613
- type: field,
6614
- hidden: ({ parent }) => !parent?.variantId
6672
+ type: field
6673
+ // hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
6615
6674
  })
6616
6675
  ) : (
6617
6676
  // Pass in the configured options, but overwrite the name
6618
6677
  {
6619
6678
  ...field,
6620
- name: "value",
6621
- hidden: ({ parent }) => !parent?.variantId
6679
+ name: "value"
6680
+ // hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
6622
6681
  }
6623
6682
  )
6624
6683
  ],
6625
6684
  preview: {
6626
6685
  select: {
6627
- variant: "variantId",
6628
- experiment: "experimentId",
6686
+ variant: variantId,
6687
+ experiment: experimentId,
6629
6688
  value: "value"
6630
6689
  }
6631
6690
  }
6632
6691
  });
6633
- }, fieldSchema = ({ fields, experiments }) => [
6634
- ...fields.map((field) => createFieldObjectType({ field })),
6635
- ...fields.map((field) => createFieldType({ field }))
6692
+ }, fieldSchema = ({
6693
+ fields,
6694
+ experimentNameOverride,
6695
+ variantNameOverride,
6696
+ variantId,
6697
+ variantArrayName,
6698
+ experimentId
6699
+ }) => [
6700
+ ...fields.map(
6701
+ (field) => createVariantType({ field, variantNameOverride, variantId, experimentId })
6702
+ ),
6703
+ ...fields.map(
6704
+ (field) => createExperimentType({
6705
+ field,
6706
+ experimentNameOverride,
6707
+ variantNameOverride,
6708
+ variantId,
6709
+ variantArrayName,
6710
+ experimentId
6711
+ })
6712
+ )
6636
6713
  ], fieldLevelExperiments = definePlugin((config) => {
6637
- const pluginConfig = { ...CONFIG_DEFAULT, ...config }, { fields, experiments } = pluginConfig;
6714
+ const pluginConfig = { ...CONFIG_DEFAULT, ...config }, { fields, experimentNameOverride, variantNameOverride } = pluginConfig, experimentId = `${experimentNameOverride}Id`, variantArrayName = `${variantNameOverride}s`, variantId = `${variantNameOverride}Id`;
6638
6715
  return {
6639
6716
  name: "sanity-personalistaion-plugin-field-level-experiments",
6640
6717
  schema: {
6641
- types: fieldSchema({ fields, experiments })
6718
+ types: fieldSchema({
6719
+ fields,
6720
+ experimentNameOverride,
6721
+ variantNameOverride,
6722
+ variantId,
6723
+ variantArrayName,
6724
+ experimentId
6725
+ })
6642
6726
  },
6643
6727
  form: {
6644
6728
  components: {
6645
6729
  input: (props) => {
6646
6730
  if (!(props.id === "root" && isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map(
6647
6731
  (field) => field.type.name
6648
- ).some((name) => name.startsWith("experiment")))
6732
+ ).some(
6733
+ (name) => name.startsWith(experimentNameOverride)
6734
+ ))
6649
6735
  return props.renderDefault(props);
6650
- const providerProps = { ...props, experimentFieldPluginConfig: pluginConfig };
6736
+ const providerProps = {
6737
+ ...props,
6738
+ experimentFieldPluginConfig: {
6739
+ ...pluginConfig,
6740
+ variantId,
6741
+ variantArrayName,
6742
+ experimentId
6743
+ }
6744
+ };
6651
6745
  return ExperimentProvider(providerProps);
6652
6746
  }
6653
6747
  }
6654
6748
  }
6655
6749
  };
6656
- }), namespace = "growthbook", pluginConfigKeys = [
6750
+ }), pluginConfigKeys = [
6657
6751
  {
6658
6752
  key: "apiKey",
6659
6753
  title: "Your secret API key"
6660
6754
  }
6661
- ], Secrets = (props) => {
6755
+ ], Secrets = (props, namespace) => {
6662
6756
  const { secrets, loading } = useSecrets(namespace), { setSecret } = useExperimentContext(), [showSettings, setShowSettings] = useState(!1);
6663
6757
  return useEffect(() => {
6664
6758
  if (!loading)
@@ -6667,7 +6761,7 @@ const createFieldType = ({
6667
6761
  /* @__PURE__ */ jsx(
6668
6762
  SettingsView,
6669
6763
  {
6670
- title: "Growthbook secret",
6764
+ title: `${namespace} api key`,
6671
6765
  namespace,
6672
6766
  keys: pluginConfigKeys,
6673
6767
  onClose: () => {
@@ -6677,62 +6771,50 @@ const createFieldType = ({
6677
6771
  ),
6678
6772
  props.renderDefault(props)
6679
6773
  ] }) : props.renderDefault(props);
6680
- }, getBooleanConversion = (value) => value === "true" ? "variant" : value === "false" ? "control" : value, getExperiments = async ({
6774
+ }, getExperiments = async ({
6681
6775
  client,
6682
- environment,
6683
- baseUrl,
6684
- project,
6685
- convertBooleans
6776
+ projectKey,
6777
+ tags
6686
6778
  }) => {
6687
- const secret = await client.fetch("*[_id == 'secrets.growthbook'][0].secrets.apiKey");
6779
+ const secret = await client.fetch("*[_id == 'secrets.launchdarkly'][0].secrets.apiKey");
6688
6780
  if (!secret) return [];
6781
+ const url = new URL(`https://app.launchdarkly.com/api/v2/flags/${projectKey}`);
6782
+ tags && url.searchParams.set("filter", `tags:${tags.join("+")}`);
6689
6783
  const featureExperiments = [];
6690
- let hasMore = !0, offset = 0;
6691
- const url = new URL(baseUrl ?? "https://api.growthbook.io/api/v1/features");
6692
- for (project && url.searchParams.set("projectId", project); hasMore; ) {
6693
- url.searchParams.set("offset", offset.toString());
6694
- const response = await fetch(url, {
6784
+ let hasMore = !0;
6785
+ const offset = 0, limit = 10;
6786
+ for (; hasMore; ) {
6787
+ url.searchParams.set("offset", offset.toString()), url.searchParams.set("limit", limit.toString());
6788
+ const responseFlags = await fetch(url, {
6695
6789
  headers: {
6696
- Authorization: `Bearer ${secret}`
6790
+ Authorization: secret
6697
6791
  }
6698
- }), { features, hasMore: responseHasMore, nextOffset } = await response.json();
6699
- hasMore = responseHasMore, offset = nextOffset, features && features.forEach((feature) => {
6700
- if (feature.archived)
6701
- return;
6702
- const experiments = feature.environments[environment]?.rules.filter(
6703
- (experiment) => experiment.type === "experiment-ref" || experiment.type === "experiment"
6704
- );
6705
- if (!experiments)
6706
- return;
6707
- const variations = /* @__PURE__ */ new Set();
6708
- experiments.forEach((experiment) => {
6709
- experiment?.variations.forEach((variant) => {
6710
- variations.add({
6711
- id: convertBooleans ? getBooleanConversion(variant.value) : variant.value,
6712
- label: convertBooleans ? getBooleanConversion(variant.value) : variant.value
6713
- });
6714
- });
6715
- });
6716
- const value = { id: feature.id, label: feature.id, variants: [...variations] };
6717
- featureExperiments.push(value);
6718
- });
6792
+ }), { items } = await responseFlags.json(), experiments = items.map((flag) => ({
6793
+ id: flag.key,
6794
+ label: flag.name,
6795
+ variants: flag.variations.map((variation) => ({
6796
+ id: variation.value,
6797
+ label: variation.name
6798
+ }))
6799
+ }));
6800
+ featureExperiments.push(...experiments), items.length !== limit && (hasMore = !1);
6719
6801
  }
6720
6802
  return featureExperiments;
6721
- }, growthbookFieldLevel = definePlugin((config) => {
6722
- const { fields, environment, project, convertBooleans, baseUrl } = config;
6803
+ }, launchDarklyFieldLevel = definePlugin((config) => {
6804
+ const { fields, projectKey, tags } = config;
6723
6805
  return {
6724
6806
  name: "sanity-growthbook-personalistaion-plugin-field-level-experiments",
6725
6807
  plugins: [
6726
6808
  fieldLevelExperiments({
6727
6809
  fields,
6728
- experiments: (client) => getExperiments({ client, environment, baseUrl, project, convertBooleans })
6810
+ experiments: (client) => getExperiments({ client, projectKey, tags })
6729
6811
  })
6730
6812
  ],
6731
6813
  form: {
6732
6814
  components: {
6733
6815
  input: (props) => !(props.id === "root" && isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map(
6734
6816
  (field) => field.type.name
6735
- ).some((name) => name.startsWith("experiment")) ? props.renderDefault(props) : Secrets(props)
6817
+ ).some((name) => name.startsWith("experiment")) ? props.renderDefault(props) : Secrets(props, "launchdarkly")
6736
6818
  }
6737
6819
  }
6738
6820
  };
@@ -6740,6 +6822,6 @@ const createFieldType = ({
6740
6822
  export {
6741
6823
  fieldLevelExperiments,
6742
6824
  flattenSchemaType,
6743
- growthbookFieldLevel
6825
+ launchDarklyFieldLevel
6744
6826
  };
6745
6827
  //# sourceMappingURL=index.mjs.map