@sanity/personalization-plugin 2.1.0 → 2.2.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.mjs CHANGED
@@ -2,10 +2,11 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { useClient, useWorkspace, useFormValue, defineDocumentFieldAction, set, unset, useDocumentOperation, isReference, isImage, isDocumentSchemaType, definePlugin, isObjectInputProps, defineType, defineField } from "sanity";
3
3
  import { Stack, Inline, Button, Select as Select$1 } from "@sanity/ui";
4
4
  import { uuid } from "@sanity/uuid";
5
- import { createContext, useMemo, useContext, useCallback, forwardRef, useState, useEffect } from "react";
5
+ import { createContext, useState, useMemo, useContext, useCallback, forwardRef, useEffect } from "react";
6
6
  import equal from "fast-deep-equal";
7
7
  import { suspend } from "suspend-react";
8
8
  import { GiSoapExperiment } from "react-icons/gi";
9
+ import { useSecrets, SettingsView } from "@sanity/studio-secrets";
9
10
  const CONFIG_DEFAULT = {
10
11
  fields: [],
11
12
  apiVersion: "2024-11-07",
@@ -16,20 +17,23 @@ const CONFIG_DEFAULT = {
16
17
  experimentId: "experimentId"
17
18
  }, ExperimentContext = createContext({
18
19
  ...CONFIG_DEFAULT,
19
- experiments: []
20
+ experiments: [],
21
+ setSecret: () => {
22
+ },
23
+ secret: void 0
20
24
  });
21
25
  function useExperimentContext() {
22
26
  return useContext(ExperimentContext);
23
27
  }
24
28
  function ExperimentProvider(props) {
25
- const { experimentFieldPluginConfig } = props, client = useClient({ apiVersion: experimentFieldPluginConfig.apiVersion }), workspace = useWorkspace(), experiments = Array.isArray(experimentFieldPluginConfig.experiments) ? experimentFieldPluginConfig.experiments : suspend(
29
+ const { experimentFieldPluginConfig } = props, [secret, setSecret] = useState(), client = useClient({ apiVersion: experimentFieldPluginConfig.apiVersion }), workspace = useWorkspace(), experiments = Array.isArray(experimentFieldPluginConfig.experiments) ? experimentFieldPluginConfig.experiments : suspend(
26
30
  // eslint-disable-next-line require-await
27
- async () => typeof experimentFieldPluginConfig.experiments == "function" ? experimentFieldPluginConfig.experiments(client) : experimentFieldPluginConfig.experiments,
28
- [workspace],
31
+ async () => typeof experimentFieldPluginConfig.experiments == "function" ? experimentFieldPluginConfig.experiments(client, secret) : experimentFieldPluginConfig.experiments,
32
+ [workspace, secret],
29
33
  { equal }
30
34
  ), context = useMemo(
31
- () => ({ ...experimentFieldPluginConfig, experiments }),
32
- [experimentFieldPluginConfig, experiments]
35
+ () => ({ ...experimentFieldPluginConfig, experiments, secret, setSecret }),
36
+ [experimentFieldPluginConfig, experiments, secret, setSecret]
33
37
  );
34
38
  return /* @__PURE__ */ jsx(ExperimentContext.Provider, { value: context, children: props.renderDefault(props) });
35
39
  }
@@ -6406,9 +6410,9 @@ const icons = {
6406
6410
  });
6407
6411
  Icon.displayName = "ForwardRef(Icon)";
6408
6412
  const useAddExperimentAction = (props) => {
6409
- const { onChange, experimentNameOverride } = props, handleAddAction = () => {
6410
- onChange([set(!0, ["active"])]);
6411
- };
6413
+ const { onChange, active, experimentNameOverride } = props, handleAddAction = useCallback(() => {
6414
+ onChange([set(!active, ["active"])]);
6415
+ }, [onChange, active]);
6412
6416
  return {
6413
6417
  title: `Add ${experimentNameOverride}`,
6414
6418
  type: "action",
@@ -6417,13 +6421,10 @@ const useAddExperimentAction = (props) => {
6417
6421
  renderAsButton: !0
6418
6422
  };
6419
6423
  }, useRemoveExperimentAction = (props) => {
6420
- const { onChange, experimentId, experimentNameOverride } = props, patchActiveFalseEvent = () => set(!1, ["active"]), patchClearEvent = () => {
6421
- const experiment = [experimentId], variants = [experimentNameOverride];
6422
- return [unset(experiment), unset(variants)];
6423
- }, handleClearAction = () => {
6424
- const clearEvents = patchClearEvent(), activeEvent = patchActiveFalseEvent();
6425
- onChange([activeEvent, ...clearEvents]);
6426
- };
6424
+ const { onChange, active, experimentId, experimentNameOverride, variantNameOverride } = props, handleClearAction = useCallback(() => {
6425
+ const activeId = ["active"], experiment = [experimentId], variants = [`${variantNameOverride}s`];
6426
+ onChange([set(!active, activeId), unset(experiment), unset(variants)]);
6427
+ }, [onChange, active, experimentId, variantNameOverride]);
6427
6428
  return {
6428
6429
  title: `Remove ${experimentNameOverride}`,
6429
6430
  type: "action",
@@ -6431,36 +6432,53 @@ const useAddExperimentAction = (props) => {
6431
6432
  onAction: handleClearAction,
6432
6433
  renderAsButton: !0
6433
6434
  };
6434
- }, newActions = ({
6435
+ }, createActions = ({
6435
6436
  onChange,
6436
6437
  inputId,
6437
6438
  active,
6438
6439
  experimentNameOverride,
6439
- experimentId
6440
+ experimentId,
6441
+ variantNameOverride
6440
6442
  }) => {
6441
6443
  const removeAction = defineDocumentFieldAction({
6442
6444
  name: `Remove ${experimentNameOverride}`,
6443
6445
  useAction: (props) => useRemoveExperimentAction({
6446
+ active: !0,
6444
6447
  onChange,
6445
6448
  experimentNameOverride,
6446
- experimentId
6449
+ experimentId,
6450
+ variantNameOverride
6447
6451
  })
6448
6452
  }), addAction = defineDocumentFieldAction({
6449
6453
  name: `Add ${experimentNameOverride}`,
6450
6454
  useAction: (props) => useAddExperimentAction({
6455
+ active: !1,
6451
6456
  onChange,
6452
6457
  experimentNameOverride
6453
6458
  })
6454
6459
  });
6455
6460
  return active ? removeAction : addAction;
6456
6461
  }, ExperimentField = (props) => {
6457
- const { onChange } = props.inputProps, { inputId, experimentNameOverride, experimentId } = props, active = props.value?.active, oldActions = props.actions || [], withActionProps = {
6458
- ...props,
6459
- actions: [
6460
- newActions({ onChange, inputId, active, experimentNameOverride, experimentId }),
6461
- ...oldActions
6462
- ]
6463
- };
6462
+ const { onChange } = props.inputProps, { inputId, experimentNameOverride, experimentId, variantNameOverride } = props, active = props.value?.active, actionProps = useMemo(
6463
+ () => ({
6464
+ onChange,
6465
+ inputId,
6466
+ active,
6467
+ experimentNameOverride,
6468
+ experimentId,
6469
+ variantNameOverride
6470
+ }),
6471
+ [onChange, inputId, active, experimentNameOverride, experimentId, variantNameOverride]
6472
+ ), memoizedActions = useMemo(() => {
6473
+ const oldActions = props.actions || [];
6474
+ return [createActions(actionProps), ...oldActions];
6475
+ }, [actionProps, props.actions]), withActionProps = useMemo(
6476
+ () => ({
6477
+ ...props,
6478
+ actions: memoizedActions
6479
+ }),
6480
+ [props, memoizedActions]
6481
+ );
6464
6482
  return props.renderDefault(withActionProps);
6465
6483
  }, Select = (props) => {
6466
6484
  const {
@@ -6492,7 +6510,7 @@ const useAddExperimentAction = (props) => {
6492
6510
  value: experiment.id
6493
6511
  })), ExperimentInput = (props) => {
6494
6512
  const { experiments } = useExperimentContext(), id = useFormValue(["_id"]), aditionalChangePath = useMemo(
6495
- () => [...props.path.slice(0, -1), props.variantNameOverride],
6513
+ () => [...props.path.slice(0, -1), `${props.variantNameOverride}s`],
6496
6514
  [props.variantNameOverride, props.path]
6497
6515
  ), subValues = useFormValue(aditionalChangePath), { patch } = useDocumentOperation(id.replace("drafts.", ""), props.schemaType.name), handleChange = useCallback(
6498
6516
  (event, onChange) => {
@@ -6580,7 +6598,8 @@ const createExperimentType = ({
6580
6598
  {
6581
6599
  ...props,
6582
6600
  experimentId,
6583
- experimentNameOverride
6601
+ experimentNameOverride,
6602
+ variantNameOverride
6584
6603
  }
6585
6604
  )
6586
6605
  },
@@ -6743,9 +6762,93 @@ const createExperimentType = ({
6743
6762
  }
6744
6763
  }
6745
6764
  };
6765
+ }), namespace = "growthbook", pluginConfigKeys = [
6766
+ {
6767
+ key: "apiKey",
6768
+ title: "Your secret API key"
6769
+ }
6770
+ ], Secrets = (props) => {
6771
+ const { secrets, loading } = useSecrets(namespace), { setSecret } = useExperimentContext(), [showSettings, setShowSettings] = useState(!1);
6772
+ return useEffect(() => {
6773
+ if (!loading)
6774
+ return !secrets && !loading ? (setSecret(void 0), setShowSettings(!0)) : (setSecret(secrets.apiKey), setShowSettings(!1));
6775
+ }, [secrets, loading, setSecret]), showSettings ? /* @__PURE__ */ jsxs(Fragment, { children: [
6776
+ /* @__PURE__ */ jsx(
6777
+ SettingsView,
6778
+ {
6779
+ title: "Growthbook secret",
6780
+ namespace,
6781
+ keys: pluginConfigKeys,
6782
+ onClose: () => {
6783
+ setShowSettings(!1);
6784
+ }
6785
+ }
6786
+ ),
6787
+ props.renderDefault(props)
6788
+ ] }) : props.renderDefault(props);
6789
+ }, getBooleanConversion = (value) => value === "true" ? "variant" : value === "false" ? "control" : value, getExperiments = async ({
6790
+ client,
6791
+ environment,
6792
+ baseUrl,
6793
+ project,
6794
+ convertBooleans
6795
+ }) => {
6796
+ const secret = await client.fetch("*[_id == 'secrets.growthbook'][0].secrets.apiKey");
6797
+ if (!secret) return [];
6798
+ const featureExperiments = [];
6799
+ let hasMore = !0, offset = 0;
6800
+ const url = new URL(baseUrl ?? "https://api.growthbook.io/api/v1/features");
6801
+ for (project && url.searchParams.set("projectId", project); hasMore; ) {
6802
+ url.searchParams.set("offset", offset.toString());
6803
+ const response = await fetch(url, {
6804
+ headers: {
6805
+ Authorization: `Bearer ${secret}`
6806
+ }
6807
+ }), { features, hasMore: responseHasMore, nextOffset } = await response.json();
6808
+ hasMore = responseHasMore, offset = nextOffset, features && features.forEach((feature) => {
6809
+ if (feature.archived)
6810
+ return;
6811
+ const experiments = feature.environments[environment]?.rules.filter(
6812
+ (experiment) => experiment.type === "experiment-ref" || experiment.type === "experiment"
6813
+ );
6814
+ if (!experiments)
6815
+ return;
6816
+ const variations = /* @__PURE__ */ new Set();
6817
+ experiments.forEach((experiment) => {
6818
+ experiment?.variations.forEach((variant) => {
6819
+ variations.add({
6820
+ id: convertBooleans ? getBooleanConversion(variant.value) : variant.value,
6821
+ label: convertBooleans ? getBooleanConversion(variant.value) : variant.value
6822
+ });
6823
+ });
6824
+ });
6825
+ const value = { id: feature.id, label: feature.id, variants: [...variations] };
6826
+ featureExperiments.push(value);
6827
+ });
6828
+ }
6829
+ return featureExperiments;
6830
+ }, growthbookFieldLevel = definePlugin((config) => {
6831
+ const { fields, environment, project, convertBooleans, baseUrl } = config;
6832
+ return {
6833
+ name: "sanity-growthbook-personalistaion-plugin-field-level-experiments",
6834
+ plugins: [
6835
+ fieldLevelExperiments({
6836
+ fields,
6837
+ experiments: (client) => getExperiments({ client, environment, baseUrl, project, convertBooleans })
6838
+ })
6839
+ ],
6840
+ form: {
6841
+ components: {
6842
+ input: (props) => !(props.id === "root" && isObjectInputProps(props)) || !flattenSchemaType(props.schemaType).map(
6843
+ (field) => field.type.name
6844
+ ).some((name) => name.startsWith("experiment")) ? props.renderDefault(props) : Secrets(props)
6845
+ }
6846
+ }
6847
+ };
6746
6848
  });
6747
6849
  export {
6748
6850
  fieldLevelExperiments,
6749
- flattenSchemaType
6851
+ flattenSchemaType,
6852
+ growthbookFieldLevel
6750
6853
  };
6751
6854
  //# sourceMappingURL=index.mjs.map