@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/dist/index.js CHANGED
@@ -1,50 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), ui = require("@sanity/ui"), uuid = require("@sanity/uuid"), react = require("react"), equal = require("fast-deep-equal"), suspendReact = require("suspend-react"), gi = require("react-icons/gi");
3
+ var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), ui = require("@sanity/ui"), uuid = require("@sanity/uuid"), react = require("react"), equal = require("fast-deep-equal"), suspendReact = require("suspend-react"), gi = require("react-icons/gi"), studioSecrets = require("@sanity/studio-secrets");
4
4
  function _interopDefaultCompat(e) {
5
5
  return e && typeof e == "object" && "default" in e ? e : { default: e };
6
6
  }
7
7
  var equal__default = /* @__PURE__ */ _interopDefaultCompat(equal);
8
8
  const CONFIG_DEFAULT = {
9
9
  fields: [],
10
- apiVersion: "2024-11-07",
11
- experimentNameOverride: "experiment",
12
- variantNameOverride: "variant",
13
- variantId: "variantId",
14
- variantArrayName: "variants",
15
- experimentId: "experimentId"
10
+ apiVersion: "2024-11-07"
16
11
  }, ExperimentContext = react.createContext({
17
12
  ...CONFIG_DEFAULT,
18
- experiments: []
13
+ experiments: [],
14
+ setSecret: () => {
15
+ },
16
+ secret: void 0
19
17
  });
20
18
  function useExperimentContext() {
21
19
  return react.useContext(ExperimentContext);
22
20
  }
23
21
  function ExperimentProvider(props) {
24
- const { experimentFieldPluginConfig } = props, client = sanity.useClient({ apiVersion: experimentFieldPluginConfig.apiVersion }), workspace = sanity.useWorkspace(), experiments = Array.isArray(experimentFieldPluginConfig.experiments) ? experimentFieldPluginConfig.experiments : suspendReact.suspend(
22
+ const { experimentFieldPluginConfig } = props, [secret, setSecret] = react.useState(), client = sanity.useClient({ apiVersion: experimentFieldPluginConfig.apiVersion }), workspace = sanity.useWorkspace(), experiments = Array.isArray(experimentFieldPluginConfig.experiments) ? experimentFieldPluginConfig.experiments : suspendReact.suspend(
25
23
  // eslint-disable-next-line require-await
26
- async () => typeof experimentFieldPluginConfig.experiments == "function" ? experimentFieldPluginConfig.experiments(client) : experimentFieldPluginConfig.experiments,
27
- [workspace],
24
+ async () => typeof experimentFieldPluginConfig.experiments == "function" ? experimentFieldPluginConfig.experiments(client, secret) : experimentFieldPluginConfig.experiments,
25
+ [workspace, secret],
28
26
  { equal: equal__default.default }
29
27
  ), context = react.useMemo(
30
- () => ({ ...experimentFieldPluginConfig, experiments }),
31
- [experimentFieldPluginConfig, experiments]
28
+ () => ({ ...experimentFieldPluginConfig, experiments, secret, setSecret }),
29
+ [experimentFieldPluginConfig, experiments, secret, setSecret]
32
30
  );
33
31
  return /* @__PURE__ */ jsxRuntime.jsx(ExperimentContext.Provider, { value: context, children: props.renderDefault(props) });
34
32
  }
35
33
  const ArrayInput = (props) => {
36
- const fieldPath = props.path.slice(0, -1), { onItemAppend, variantName, variantId, experimentId } = props, experimentValue = sanity.useFormValue([...fieldPath, experimentId]), { experiments } = useExperimentContext(), handleClick = react.useCallback(
34
+ const fieldPath = props.path.slice(0, -1), experimentId = sanity.useFormValue([...fieldPath, "experimentId"]), { experiments } = useExperimentContext(), { onItemAppend, objectName } = props, handleClick = react.useCallback(
37
35
  async (variant) => {
38
36
  const item = {
39
37
  _key: uuid.uuid(),
40
- [variantId]: variant.id,
41
- [experimentId]: experimentValue,
42
- _type: variantName
38
+ variantId: variant.id,
39
+ experimentId,
40
+ _type: objectName
43
41
  };
44
42
  onItemAppend(item);
45
43
  },
46
- [variantId, experimentId, experimentValue, variantName, onItemAppend]
47
- ), filteredVariants = experiments.find((option) => option.id === experimentValue)?.variants || [], usedVariants = (props.value || [])?.map((variant) => variant[variantId]);
44
+ [experimentId, objectName, onItemAppend]
45
+ ), filteredVariants = experiments.find((option) => option.id === experimentId)?.variants || [], usedVariants = props.value?.map((variant) => variant.variantId);
48
46
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
49
47
  props.renderDefault({ ...props, arrayFunctions: () => null }),
50
48
  /* @__PURE__ */ jsxRuntime.jsx(ui.Inline, { space: 1, children: filteredVariants.map((variant) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -55,7 +53,7 @@ const ArrayInput = (props) => {
55
53
  disabled: usedVariants?.includes(variant.id),
56
54
  onClick: () => handleClick(variant)
57
55
  },
58
- `${experimentValue}-${variant.id}`
56
+ `${experimentId}-${variant.id}`
59
57
  )) })
60
58
  ] });
61
59
  }, AccessDeniedIcon = react.forwardRef(function(props, ref) {
@@ -6405,60 +6403,40 @@ const icons = {
6405
6403
  });
6406
6404
  Icon.displayName = "ForwardRef(Icon)";
6407
6405
  const useAddExperimentAction = (props) => {
6408
- const { onChange, experimentNameOverride } = props, handleAddAction = () => {
6409
- onChange([sanity.set(!0, ["active"])]);
6410
- };
6406
+ const patchActiveEvent = react.useMemo(() => sanity.set(!0, ["active"]), []), handleAction = react.useCallback(() => {
6407
+ props.onChange([patchActiveEvent]);
6408
+ }, [patchActiveEvent, props]);
6411
6409
  return {
6412
- title: `Add ${experimentNameOverride}`,
6410
+ title: "Add experiment",
6413
6411
  type: "action",
6414
6412
  icon: gi.GiSoapExperiment,
6415
- onAction: handleAddAction,
6413
+ onAction: handleAction,
6416
6414
  renderAsButton: !0
6417
6415
  };
6418
6416
  }, useRemoveExperimentAction = (props) => {
6419
- const { onChange, experimentId, experimentNameOverride } = props, patchActiveFalseEvent = () => sanity.set(!1, ["active"]), patchClearEvent = () => {
6420
- const experiment = [experimentId], variants = [experimentNameOverride];
6421
- return [sanity.unset(experiment), sanity.unset(variants)];
6422
- }, handleClearAction = () => {
6423
- const clearEvents = patchClearEvent(), activeEvent = patchActiveFalseEvent();
6424
- onChange([activeEvent, ...clearEvents]);
6425
- };
6417
+ const patchActiveEvent = react.useMemo(() => sanity.set(!1, ["active"]), []), patchClearEvent = react.useMemo(() => {
6418
+ const experimentId = ["experimentId"], variants = ["variants"];
6419
+ return [sanity.unset(experimentId), sanity.unset(variants)];
6420
+ }, []), handleAction = react.useCallback(() => {
6421
+ props.onChange([patchActiveEvent, ...patchClearEvent]);
6422
+ }, [patchActiveEvent, patchClearEvent, props]);
6426
6423
  return {
6427
- title: `Remove ${experimentNameOverride}`,
6424
+ title: "Remove experiment",
6428
6425
  type: "action",
6429
6426
  icon: CloseIcon,
6430
- onAction: handleClearAction,
6427
+ onAction: handleAction,
6431
6428
  renderAsButton: !0
6432
6429
  };
6433
- }, newActions = ({
6434
- onChange,
6435
- inputId,
6436
- active,
6437
- experimentNameOverride,
6438
- experimentId
6439
- }) => {
6440
- const removeAction = sanity.defineDocumentFieldAction({
6441
- name: `Remove ${experimentNameOverride}`,
6442
- useAction: (props) => useRemoveExperimentAction({
6443
- onChange,
6444
- experimentNameOverride,
6445
- experimentId
6446
- })
6447
- }), addAction = sanity.defineDocumentFieldAction({
6448
- name: `Add ${experimentNameOverride}`,
6449
- useAction: (props) => useAddExperimentAction({
6450
- onChange,
6451
- experimentNameOverride
6452
- })
6453
- });
6454
- return active ? removeAction : addAction;
6455
- }, ExperimentField = (props) => {
6456
- const { onChange } = props.inputProps, { inputId, experimentNameOverride, experimentId } = props, active = props.value?.active, oldActions = props.actions || [], withActionProps = {
6430
+ }, newActions = ({ onChange, inputId, active }) => active ? sanity.defineDocumentFieldAction({
6431
+ name: "Experiment",
6432
+ useAction: (props) => useRemoveExperimentAction({ ...props, onChange, inputId })
6433
+ }) : sanity.defineDocumentFieldAction({
6434
+ name: "Experiment",
6435
+ useAction: (props) => useAddExperimentAction({ ...props, onChange, inputId })
6436
+ }), ExperimentField = (props) => {
6437
+ const { onChange } = props.inputProps, { inputId } = props, active = props.value?.active, oldActions = props.actions || [], withActionProps = {
6457
6438
  ...props,
6458
- actions: [
6459
- newActions({ onChange, inputId, active, experimentNameOverride, experimentId }),
6460
- ...oldActions
6461
- ]
6439
+ actions: [newActions({ onChange, inputId, active }), ...oldActions]
6462
6440
  };
6463
6441
  return props.renderDefault(withActionProps);
6464
6442
  }, Select = (props) => {
@@ -6490,10 +6468,7 @@ const useAddExperimentAction = (props) => {
6490
6468
  title: experiment.label,
6491
6469
  value: experiment.id
6492
6470
  })), ExperimentInput = (props) => {
6493
- const { experiments } = useExperimentContext(), id = sanity.useFormValue(["_id"]), aditionalChangePath = react.useMemo(
6494
- () => [...props.path.slice(0, -1), props.variantNameOverride],
6495
- [props.variantNameOverride, props.path]
6496
- ), subValues = sanity.useFormValue(aditionalChangePath), { patch } = sanity.useDocumentOperation(id.replace("drafts.", ""), props.schemaType.name), handleChange = react.useCallback(
6471
+ const { experiments } = useExperimentContext(), id = sanity.useFormValue(["_id"]), aditionalChangePath = react.useMemo(() => [...props.path.slice(0, -1), "variants"], [props.path]), subValues = sanity.useFormValue(aditionalChangePath), { patch } = sanity.useDocumentOperation(id.replace("drafts.", ""), props.schemaType.name), handleChange = react.useCallback(
6497
6472
  (event, onChange) => {
6498
6473
  const inputValue = event.currentTarget.value;
6499
6474
  if (onChange(inputValue ? sanity.set(inputValue) : sanity.unset()), subValues) {
@@ -6553,27 +6528,15 @@ function extractInnerFields(fields, path, maxDepth) {
6553
6528
  return [...acc, thisFieldWithPath];
6554
6529
  }, []);
6555
6530
  }
6556
- const createExperimentType = ({
6557
- field,
6558
- experimentNameOverride,
6559
- variantNameOverride,
6560
- variantId,
6561
- variantArrayName,
6562
- experimentId
6531
+ const createFieldType = ({
6532
+ field
6563
6533
  }) => {
6564
- const typeName = typeof field == "string" ? field : field.name, usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1), variantName = `${variantNameOverride}${usedName}`;
6534
+ const typeName = typeof field == "string" ? field : field.name, usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1), objectName = `variant${usedName}`;
6565
6535
  return sanity.defineType({
6566
- name: `${experimentNameOverride}${usedName}`,
6536
+ name: `experiment${usedName}`,
6567
6537
  type: "object",
6568
6538
  components: {
6569
- field: (props) => /* @__PURE__ */ jsxRuntime.jsx(
6570
- ExperimentField,
6571
- {
6572
- ...props,
6573
- experimentId,
6574
- experimentNameOverride
6575
- }
6576
- )
6539
+ field: ExperimentField
6577
6540
  },
6578
6541
  fields: [
6579
6542
  typeof field == "string" ? (
@@ -6595,47 +6558,37 @@ const createExperimentType = ({
6595
6558
  hidden: !0
6596
6559
  }),
6597
6560
  sanity.defineField({
6598
- name: experimentId,
6561
+ title: "Experiment",
6562
+ name: "experimentId",
6599
6563
  type: "string",
6600
6564
  components: {
6601
- input: (props) => /* @__PURE__ */ jsxRuntime.jsx(ExperimentInput, { ...props, variantNameOverride })
6565
+ input: ExperimentInput
6602
6566
  },
6603
6567
  hidden: ({ parent }) => !parent?.active
6604
6568
  }),
6605
6569
  sanity.defineField({
6606
- name: variantArrayName,
6570
+ name: "variants",
6607
6571
  type: "array",
6608
- hidden: ({ parent }) => !parent?.[experimentId],
6572
+ hidden: ({ parent }) => !parent?.experimentId,
6609
6573
  components: {
6610
- input: (props) => /* @__PURE__ */ jsxRuntime.jsx(
6611
- ArrayInput,
6612
- {
6613
- ...props,
6614
- variantName,
6615
- variantId,
6616
- experimentId
6617
- }
6618
- )
6574
+ input: (props) => /* @__PURE__ */ jsxRuntime.jsx(ArrayInput, { ...props, objectName })
6619
6575
  },
6620
6576
  of: [
6621
6577
  sanity.defineField({
6622
- name: variantName,
6623
- type: variantName
6578
+ name: objectName,
6579
+ type: objectName
6624
6580
  })
6625
6581
  ]
6626
6582
  })
6627
6583
  ]
6628
6584
  });
6629
- }, createVariantType = ({
6630
- field,
6631
- variantNameOverride,
6632
- variantId,
6633
- experimentId
6585
+ }, createFieldObjectType = ({
6586
+ field
6634
6587
  }) => {
6635
6588
  const typeName = typeof field == "string" ? field : field.name, usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1);
6636
6589
  return sanity.defineType({
6637
- name: `${variantNameOverride}${usedName}`,
6638
- title: `${variantNameOverride} array ${usedName}`,
6590
+ name: `variant${usedName}`,
6591
+ title: `Experiment array ${usedName}`,
6639
6592
  type: "object",
6640
6593
  components: {
6641
6594
  preview: VariantPreview
@@ -6643,97 +6596,146 @@ const createExperimentType = ({
6643
6596
  fields: [
6644
6597
  {
6645
6598
  type: "string",
6646
- name: variantId,
6599
+ name: "variantId",
6647
6600
  readOnly: !0
6648
6601
  },
6649
6602
  {
6650
6603
  type: "string",
6651
- name: experimentId,
6604
+ name: "experimentId",
6652
6605
  hidden: !0
6653
6606
  },
6654
6607
  typeof field == "string" ? (
6655
6608
  // Define a simple field if all we have is the name as a string
6656
6609
  sanity.defineField({
6657
6610
  name: "value",
6658
- type: field
6659
- // hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
6611
+ type: field,
6612
+ hidden: ({ parent }) => !parent?.variantId
6660
6613
  })
6661
6614
  ) : (
6662
6615
  // Pass in the configured options, but overwrite the name
6663
6616
  {
6664
6617
  ...field,
6665
- name: "value"
6666
- // hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
6618
+ name: "value",
6619
+ hidden: ({ parent }) => !parent?.variantId
6667
6620
  }
6668
6621
  )
6669
6622
  ],
6670
6623
  preview: {
6671
6624
  select: {
6672
- variant: variantId,
6673
- experiment: experimentId,
6625
+ variant: "variantId",
6626
+ experiment: "experimentId",
6674
6627
  value: "value"
6675
6628
  }
6676
6629
  }
6677
6630
  });
6678
- }, fieldSchema = ({
6679
- fields,
6680
- experimentNameOverride,
6681
- variantNameOverride,
6682
- variantId,
6683
- variantArrayName,
6684
- experimentId
6685
- }) => [
6686
- ...fields.map(
6687
- (field) => createVariantType({ field, variantNameOverride, variantId, experimentId })
6688
- ),
6689
- ...fields.map(
6690
- (field) => createExperimentType({
6691
- field,
6692
- experimentNameOverride,
6693
- variantNameOverride,
6694
- variantId,
6695
- variantArrayName,
6696
- experimentId
6697
- })
6698
- )
6631
+ }, fieldSchema = ({ fields, experiments }) => [
6632
+ ...fields.map((field) => createFieldObjectType({ field })),
6633
+ ...fields.map((field) => createFieldType({ field }))
6699
6634
  ], fieldLevelExperiments = sanity.definePlugin((config) => {
6700
- const pluginConfig = { ...CONFIG_DEFAULT, ...config }, { fields, experimentNameOverride, variantNameOverride } = pluginConfig, experimentId = `${experimentNameOverride}Id`, variantArrayName = `${variantNameOverride}s`, variantId = `${variantNameOverride}Id`;
6635
+ const pluginConfig = { ...CONFIG_DEFAULT, ...config }, { fields, experiments } = pluginConfig;
6701
6636
  return {
6702
6637
  name: "sanity-personalistaion-plugin-field-level-experiments",
6703
6638
  schema: {
6704
- types: fieldSchema({
6705
- fields,
6706
- experimentNameOverride,
6707
- variantNameOverride,
6708
- variantId,
6709
- variantArrayName,
6710
- experimentId
6711
- })
6639
+ types: fieldSchema({ fields, experiments })
6712
6640
  },
6713
6641
  form: {
6714
6642
  components: {
6715
6643
  input: (props) => {
6716
6644
  if (!(props.id === "root" && sanity.isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map(
6717
6645
  (field) => field.type.name
6718
- ).some(
6719
- (name) => name.startsWith(experimentNameOverride)
6720
- ))
6646
+ ).some((name) => name.startsWith("experiment")))
6721
6647
  return props.renderDefault(props);
6722
- const providerProps = {
6723
- ...props,
6724
- experimentFieldPluginConfig: {
6725
- ...pluginConfig,
6726
- variantId,
6727
- variantArrayName,
6728
- experimentId
6729
- }
6730
- };
6648
+ const providerProps = { ...props, experimentFieldPluginConfig: pluginConfig };
6731
6649
  return ExperimentProvider(providerProps);
6732
6650
  }
6733
6651
  }
6734
6652
  }
6735
6653
  };
6654
+ }), namespace = "growthbook", pluginConfigKeys = [
6655
+ {
6656
+ key: "apiKey",
6657
+ title: "Your secret API key"
6658
+ }
6659
+ ], Secrets = (props) => {
6660
+ const { secrets, loading } = studioSecrets.useSecrets(namespace), { setSecret } = useExperimentContext(), [showSettings, setShowSettings] = react.useState(!1);
6661
+ return react.useEffect(() => {
6662
+ if (!loading)
6663
+ return !secrets && !loading ? (setSecret(void 0), setShowSettings(!0)) : (setSecret(secrets.apiKey), setShowSettings(!1));
6664
+ }, [secrets, loading, setSecret]), showSettings ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6665
+ /* @__PURE__ */ jsxRuntime.jsx(
6666
+ studioSecrets.SettingsView,
6667
+ {
6668
+ title: "Growthbook secret",
6669
+ namespace,
6670
+ keys: pluginConfigKeys,
6671
+ onClose: () => {
6672
+ setShowSettings(!1);
6673
+ }
6674
+ }
6675
+ ),
6676
+ props.renderDefault(props)
6677
+ ] }) : props.renderDefault(props);
6678
+ }, getBooleanConversion = (value) => value === "true" ? "variant" : value === "false" ? "control" : value, getExperiments = async ({
6679
+ client,
6680
+ environment,
6681
+ baseUrl,
6682
+ project,
6683
+ convertBooleans
6684
+ }) => {
6685
+ const secret = await client.fetch("*[_id == 'secrets.growthbook'][0].secrets.apiKey");
6686
+ if (!secret) return [];
6687
+ const featureExperiments = [];
6688
+ let hasMore = !0, offset = 0;
6689
+ const url = new URL(baseUrl ?? "https://api.growthbook.io/api/v1/features");
6690
+ for (project && url.searchParams.set("projectId", project); hasMore; ) {
6691
+ url.searchParams.set("offset", offset.toString());
6692
+ const response = await fetch(url, {
6693
+ headers: {
6694
+ Authorization: `Bearer ${secret}`
6695
+ }
6696
+ }), { features, hasMore: responseHasMore, nextOffset } = await response.json();
6697
+ hasMore = responseHasMore, offset = nextOffset, features && features.forEach((feature) => {
6698
+ if (feature.archived)
6699
+ return;
6700
+ const experiments = feature.environments[environment]?.rules.filter(
6701
+ (experiment) => experiment.type === "experiment-ref" || experiment.type === "experiment"
6702
+ );
6703
+ if (!experiments)
6704
+ return;
6705
+ const variations = /* @__PURE__ */ new Set();
6706
+ experiments.forEach((experiment) => {
6707
+ experiment?.variations.forEach((variant) => {
6708
+ variations.add({
6709
+ id: convertBooleans ? getBooleanConversion(variant.value) : variant.value,
6710
+ label: convertBooleans ? getBooleanConversion(variant.value) : variant.value
6711
+ });
6712
+ });
6713
+ });
6714
+ const value = { id: feature.id, label: feature.id, variants: [...variations] };
6715
+ featureExperiments.push(value);
6716
+ });
6717
+ }
6718
+ return featureExperiments;
6719
+ }, growthbookFieldLevel = sanity.definePlugin((config) => {
6720
+ const { fields, environment, project, convertBooleans, baseUrl } = config;
6721
+ return {
6722
+ name: "sanity-growthbook-personalistaion-plugin-field-level-experiments",
6723
+ plugins: [
6724
+ fieldLevelExperiments({
6725
+ fields,
6726
+ experiments: (client) => getExperiments({ client, environment, baseUrl, project, convertBooleans })
6727
+ })
6728
+ ],
6729
+ form: {
6730
+ components: {
6731
+ input: (props) => !(props.id === "root" && sanity.isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map(
6732
+ (field) => field.type.name
6733
+ ).some((name) => name.startsWith("experiment")) ? props.renderDefault(props) : Secrets(props)
6734
+ }
6735
+ }
6736
+ };
6736
6737
  });
6737
6738
  exports.fieldLevelExperiments = fieldLevelExperiments;
6738
6739
  exports.flattenSchemaType = flattenSchemaType;
6740
+ exports.growthbookFieldLevel = growthbookFieldLevel;
6739
6741
  //# sourceMappingURL=index.js.map