@builder.io/sdk-svelte 0.2.3 → 0.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.
Files changed (94) hide show
  1. package/package/--package.json +10 -5
  2. package/package/CHANGELOG.md +17 -0
  3. package/package/blocks/columns/columns.svelte +1 -1
  4. package/package/blocks/symbol/symbol.svelte +1 -1
  5. package/package/components/render-block/block-styles.svelte +3 -1
  6. package/package/components/render-block/render-block.helpers.d.ts +0 -1
  7. package/package/components/render-block/render-block.helpers.js +8 -20
  8. package/package/components/render-block/render-block.svelte +25 -19
  9. package/package/components/render-block/render-repeated-block.svelte +3 -2
  10. package/package/components/render-content/components/render-styles.svelte +1 -1
  11. package/package/components/render-content/render-content.svelte +99 -10
  12. package/package/components/render-content/render-content.svelte.d.ts +5 -0
  13. package/package/components/render-content/render-content.types.d.ts +11 -2
  14. package/package/components/render-content/wrap-component-ref.d.ts +6 -0
  15. package/package/components/render-content/wrap-component-ref.js +6 -0
  16. package/package/components/render-content-variants/helpers.d.ts +17 -0
  17. package/package/components/render-content-variants/helpers.js +182 -0
  18. package/package/components/render-content-variants/render-content-variants.svelte +70 -0
  19. package/package/components/render-content-variants/render-content-variants.svelte.d.ts +23 -0
  20. package/package/components/render-inlined-styles.svelte +3 -15
  21. package/package/components/render-inlined-styles.svelte.d.ts +2 -0
  22. package/package/constants/sdk-version.d.ts +1 -0
  23. package/package/constants/sdk-version.js +1 -0
  24. package/package/context/builder.context.d.ts +3 -2
  25. package/package/context/builder.context.js +3 -2
  26. package/package/context/types.d.ts +17 -2
  27. package/package/functions/evaluate.d.ts +4 -3
  28. package/package/functions/evaluate.js +23 -2
  29. package/package/functions/evaluate.test.d.ts +1 -0
  30. package/package/functions/evaluate.test.js +17 -0
  31. package/package/functions/get-block-actions-handler.d.ts +1 -1
  32. package/package/functions/get-block-actions-handler.js +3 -1
  33. package/package/functions/get-block-actions.d.ts +1 -1
  34. package/package/functions/get-content/generate-content-url.js +2 -2
  35. package/package/functions/get-content/generate-content-url.test.js +15 -0
  36. package/package/functions/get-content/index.d.ts +7 -2
  37. package/package/functions/get-content/index.js +43 -20
  38. package/package/functions/get-content/types.d.ts +6 -0
  39. package/package/functions/get-processed-block.d.ts +2 -2
  40. package/package/functions/get-processed-block.js +16 -4
  41. package/package/functions/get-processed-block.test.js +3 -1
  42. package/package/helpers/ab-tests.d.ts +8 -7
  43. package/package/helpers/ab-tests.js +103 -4
  44. package/package/helpers/canTrack.d.ts +1 -0
  45. package/package/helpers/canTrack.js +2 -0
  46. package/package/helpers/cookie.d.ts +7 -3
  47. package/package/helpers/cookie.js +9 -6
  48. package/package/helpers/logger.d.ts +1 -0
  49. package/package/helpers/logger.js +1 -0
  50. package/package/index-helpers/blocks-exports.d.ts +1 -1
  51. package/package/index-helpers/blocks-exports.js +1 -1
  52. package/package/index.d.ts +8 -7
  53. package/package/index.js +6 -7
  54. package/package/scripts/init-editing.d.ts +1 -0
  55. package/package/scripts/init-editing.js +2 -0
  56. package/package/types/builder-content.d.ts +1 -3
  57. package/package.json +4 -8
  58. package/src/blocks/columns/columns.svelte +1 -5
  59. package/src/blocks/symbol/symbol.svelte +1 -1
  60. package/src/components/render-block/block-styles.svelte +3 -1
  61. package/src/components/render-block/render-block.helpers.ts +9 -31
  62. package/src/components/render-block/render-block.svelte +24 -19
  63. package/src/components/render-block/render-repeated-block.svelte +3 -2
  64. package/src/components/render-content/components/render-styles.svelte +1 -1
  65. package/src/components/render-content/render-content.svelte +101 -10
  66. package/src/components/render-content/render-content.types.ts +11 -2
  67. package/src/components/render-content/wrap-component-ref.ts +6 -0
  68. package/src/components/render-content-variants/helpers.ts +245 -0
  69. package/src/components/render-content-variants/render-content-variants.svelte +83 -0
  70. package/src/components/render-inlined-styles.svelte +3 -16
  71. package/src/constants/sdk-version.ts +1 -0
  72. package/src/context/builder.context.ts +3 -2
  73. package/src/context/types.ts +17 -2
  74. package/src/functions/evaluate.test.ts +21 -0
  75. package/src/functions/evaluate.ts +46 -4
  76. package/src/functions/get-block-actions-handler.ts +7 -2
  77. package/src/functions/get-block-actions.ts +4 -1
  78. package/src/functions/get-content/generate-content-url.test.ts +17 -0
  79. package/src/functions/get-content/generate-content-url.ts +2 -1
  80. package/src/functions/get-content/index.ts +57 -26
  81. package/src/functions/get-content/types.ts +7 -0
  82. package/src/functions/get-processed-block.test.ts +3 -1
  83. package/src/functions/get-processed-block.ts +28 -6
  84. package/src/helpers/ab-tests.ts +164 -11
  85. package/src/helpers/canTrack.ts +4 -0
  86. package/src/helpers/cookie.ts +16 -11
  87. package/src/helpers/logger.ts +1 -0
  88. package/src/index-helpers/blocks-exports.ts +1 -1
  89. package/src/index.ts +17 -8
  90. package/src/scripts/init-editing.ts +3 -0
  91. package/src/types/builder-content.ts +1 -3
  92. package/package/functions/get-content/ab-testing.d.ts +0 -5
  93. package/package/functions/get-content/ab-testing.js +0 -78
  94. package/src/functions/get-content/ab-testing.ts +0 -132
@@ -1,18 +1,26 @@
1
- import type { BuilderContextInterface } from '../context/types.js';
1
+ import type {
2
+ BuilderContextInterface,
3
+ BuilderRenderState,
4
+ } from '../context/types.js';
2
5
  import { isBrowser } from './is-browser.js';
3
6
  import { isEditing } from './is-editing.js';
4
7
 
5
8
  export function evaluate({
6
9
  code,
7
10
  context,
8
- state,
11
+ localState,
12
+ rootState,
13
+ rootSetState,
9
14
  event,
10
15
  isExpression = true,
11
16
  }: {
12
17
  code: string;
13
18
  event?: Event;
14
19
  isExpression?: boolean;
15
- } & Pick<BuilderContextInterface, 'state' | 'context'>): any {
20
+ } & Pick<
21
+ BuilderContextInterface,
22
+ 'localState' | 'context' | 'rootState' | 'rootSetState'
23
+ >): any {
16
24
  if (code === '') {
17
25
  console.warn('Skipping evaluation of empty code block.');
18
26
  return;
@@ -45,7 +53,13 @@ export function evaluate({
45
53
  'context',
46
54
  'event',
47
55
  useCode
48
- )(builder, builder, state, context, event);
56
+ )(
57
+ builder,
58
+ builder,
59
+ flattenState(rootState, localState, rootSetState),
60
+ context,
61
+ event
62
+ );
49
63
  } catch (e) {
50
64
  console.warn(
51
65
  'Builder custom code error: \n While Evaluating: \n ',
@@ -55,3 +69,31 @@ export function evaluate({
55
69
  );
56
70
  }
57
71
  }
72
+
73
+ export function flattenState(
74
+ rootState: Record<string | symbol, any>,
75
+ localState: Record<string | symbol, any> | undefined,
76
+ rootSetState: ((rootState: BuilderRenderState) => void) | undefined
77
+ ): BuilderRenderState {
78
+ if (rootState === localState) {
79
+ throw new Error('rootState === localState');
80
+ }
81
+ return new Proxy(rootState, {
82
+ get: (_, prop) => {
83
+ if (localState && prop in localState) {
84
+ return localState[prop];
85
+ }
86
+ return rootState[prop as string];
87
+ },
88
+ set: (_, prop, value) => {
89
+ if (localState && prop in localState) {
90
+ throw new Error(
91
+ 'Writing to local state is not allowed as it is read-only.'
92
+ );
93
+ }
94
+ rootState[prop as string] = value;
95
+ rootSetState?.(rootState);
96
+ return true;
97
+ },
98
+ });
99
+ }
@@ -4,7 +4,10 @@ import { evaluate } from './evaluate.js';
4
4
 
5
5
  type Options = {
6
6
  block: BuilderBlock;
7
- } & Pick<BuilderContextInterface, 'state' | 'context'>;
7
+ } & Pick<
8
+ BuilderContextInterface,
9
+ 'localState' | 'context' | 'rootState' | 'rootSetState'
10
+ >;
8
11
 
9
12
  type EventHandler = (event: Event) => any;
10
13
 
@@ -14,7 +17,9 @@ export const createEventHandler =
14
17
  evaluate({
15
18
  code: value,
16
19
  context: options.context,
17
- state: options.state,
20
+ localState: options.localState,
21
+ rootState: options.rootState,
22
+ rootSetState: options.rootSetState,
18
23
  event,
19
24
  isExpression: false,
20
25
  });
@@ -8,7 +8,10 @@ type Actions = { [index: string]: (event: Event) => any };
8
8
  export function getBlockActions(
9
9
  options: {
10
10
  block: BuilderBlock;
11
- } & Pick<BuilderContextInterface, 'state' | 'context'>
11
+ } & Pick<
12
+ BuilderContextInterface,
13
+ 'localState' | 'context' | 'rootState' | 'rootSetState'
14
+ >
12
15
  ): Actions {
13
16
  const obj: Actions = {};
14
17
  const optionActions = options.block.actions ?? {};
@@ -92,4 +92,21 @@ describe('Generate Content URL', () => {
92
92
  `Invalid apiVersion: expected 'v2' or 'v3', received 'INVALID_API_VERSION'`
93
93
  );
94
94
  });
95
+
96
+ test('generate content url with enrich option true', () => {
97
+ const output = generateContentUrl({
98
+ apiKey: testKey,
99
+ model: testModel,
100
+ enrich: true,
101
+ });
102
+ expect(output).toMatchSnapshot();
103
+ });
104
+
105
+ test('generate content url with enrich option not present', () => {
106
+ const output = generateContentUrl({
107
+ apiKey: testKey,
108
+ model: testModel,
109
+ });
110
+ expect(output).toMatchSnapshot();
111
+ });
95
112
  });
@@ -15,6 +15,7 @@ export const generateContentUrl = (options: GetContentOptions): URL => {
15
15
  model,
16
16
  apiKey,
17
17
  includeRefs = true,
18
+ enrich,
18
19
  locale,
19
20
  apiVersion = DEFAULT_API_VERSION,
20
21
  } = options;
@@ -32,7 +33,7 @@ export const generateContentUrl = (options: GetContentOptions): URL => {
32
33
  const url = new URL(
33
34
  `https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${
34
35
  locale ? `&locale=${locale}` : ''
35
- }`
36
+ }${enrich ? `&enrich=${enrich}` : ''}`
36
37
  );
37
38
 
38
39
  const queryOptions = {
@@ -1,60 +1,91 @@
1
+ import { TARGET } from '../../constants/target.js';
2
+ import { handleABTesting } from '../../helpers/ab-tests.js';
3
+ import { getDefaultCanTrack } from '../../helpers/canTrack.js';
1
4
  import { logger } from '../../helpers/logger.js';
2
5
  import type { BuilderContent } from '../../types/builder-content.js';
3
6
  import { fetch } from '../get-fetch.js';
4
- import { handleABTesting } from './ab-testing.js';
7
+ import { isBrowser } from '../is-browser.js';
5
8
  import { generateContentUrl } from './generate-content-url.js';
6
9
  import type { GetContentOptions } from './types.js';
7
10
 
11
+ const checkContentHasResults = (
12
+ content: ContentResponse
13
+ ): content is ContentResults => 'results' in content;
14
+
8
15
  export async function getContent(
9
16
  options: GetContentOptions
10
17
  ): Promise<BuilderContent | null> {
11
18
  const allContent = await getAllContent({ ...options, limit: 1 });
12
- if (allContent && 'results' in allContent) {
13
- return allContent?.results[0] || null;
19
+ if (allContent && checkContentHasResults(allContent)) {
20
+ return allContent.results[0] || null;
14
21
  }
15
22
 
16
23
  return null;
17
24
  }
18
25
 
26
+ type ContentResults = {
27
+ results: BuilderContent[];
28
+ };
29
+
19
30
  type ContentResponse =
20
- | {
21
- results: BuilderContent[];
22
- }
31
+ | ContentResults
23
32
  | {
24
33
  status: number;
25
34
  message: string;
26
35
  };
27
36
 
37
+ const fetchContent = async (options: GetContentOptions) => {
38
+ const url = generateContentUrl(options);
39
+
40
+ const res = await fetch(url.href);
41
+ const content = await (res.json() as Promise<ContentResponse>);
42
+ return content;
43
+ };
44
+
45
+ /**
46
+ * Exported only for testing purposes. Should not be used directly.
47
+ */
48
+ export const processContentResult = async (
49
+ options: GetContentOptions,
50
+ content: ContentResults
51
+ ) => {
52
+ const canTrack = getDefaultCanTrack(options.canTrack);
53
+
54
+ if (!canTrack) return content;
55
+ if (!(isBrowser() || TARGET === 'reactNative')) return content;
56
+
57
+ /**
58
+ * For client-side navigations, it is ideal to handle AB testing at this point instead of using our
59
+ * complex multi-rendering variants approach, which is only needed for SSR'd content.
60
+ *
61
+ * This is also where react-native would handle AB testing.
62
+ */
63
+ try {
64
+ const newResults: BuilderContent[] = [];
65
+ for (const item of content.results) {
66
+ newResults.push(await handleABTesting({ item, canTrack }));
67
+ }
68
+ content.results = newResults;
69
+ } catch (e) {
70
+ logger.error('Could not process A/B tests. ', e);
71
+ }
72
+
73
+ return content;
74
+ };
75
+
28
76
  export async function getAllContent(
29
77
  options: GetContentOptions
30
78
  ): Promise<ContentResponse | null> {
31
79
  try {
32
80
  const url = generateContentUrl(options);
81
+ const content = await fetchContent(options);
33
82
 
34
- const res = await fetch(url.href);
35
- const content = await (res.json() as Promise<ContentResponse>);
36
-
37
- if ('status' in content && !('results' in content)) {
83
+ if (!checkContentHasResults(content)) {
38
84
  logger.error('Error fetching data. ', { url, content, options });
39
85
  return content;
40
86
  }
41
87
 
42
- const canTrack = options.canTrack !== false;
43
- try {
44
- if (
45
- canTrack &&
46
- // This makes sure we have a non-error response with the results array.
47
- Array.isArray(content.results)
48
- ) {
49
- for (const item of content.results) {
50
- await handleABTesting({ item, canTrack });
51
- }
52
- }
53
- } catch (e) {
54
- logger.error('Could not setup A/B testing. ', e);
55
- }
56
-
57
- return content;
88
+ return processContentResult(options, content);
58
89
  } catch (error) {
59
90
  logger.error('Error fetching data. ', error);
60
91
  return null;
@@ -23,6 +23,7 @@ export interface GetContentOptions {
23
23
  /**
24
24
  * If set to `true`, it will lazy load symbols/references.
25
25
  * If set to `false`, it will render the entire content tree eagerly.
26
+ * @deprecated use `enrich` instead
26
27
  */
27
28
  noTraverse?: boolean;
28
29
 
@@ -36,9 +37,15 @@ export interface GetContentOptions {
36
37
 
37
38
  /**
38
39
  * Include references in the response. Defaults to `true`.
40
+ * @deprecated use `enrich` instead
39
41
  */
40
42
  includeRefs?: boolean;
41
43
 
44
+ /**
45
+ * Include multilevel references in the response.
46
+ */
47
+ enrich?: boolean;
48
+
42
49
  /**
43
50
  * If provided, the API will auto-resolve localized objects to the value of this `locale` key.
44
51
  */
@@ -21,7 +21,9 @@ test('Can process bindings', () => {
21
21
  const processed = getProcessedBlock({
22
22
  block,
23
23
  context: {},
24
- state: { test: 'hello' },
24
+ rootState: { test: 'hello' },
25
+ rootSetState: undefined,
26
+ localState: undefined,
25
27
  shouldEvaluateBindings: true,
26
28
  });
27
29
  expect(processed).not.toEqual(block);
@@ -8,10 +8,15 @@ import { transformBlock } from './transform-block.js';
8
8
  const evaluateBindings = ({
9
9
  block,
10
10
  context,
11
- state,
11
+ localState,
12
+ rootState,
13
+ rootSetState,
12
14
  }: {
13
15
  block: BuilderBlock;
14
- } & Pick<BuilderContextInterface, 'state' | 'context'>): BuilderBlock => {
16
+ } & Pick<
17
+ BuilderContextInterface,
18
+ 'localState' | 'context' | 'rootState' | 'rootSetState'
19
+ >): BuilderBlock => {
15
20
  if (!block.bindings) {
16
21
  return block;
17
22
  }
@@ -24,7 +29,13 @@ const evaluateBindings = ({
24
29
 
25
30
  for (const binding in block.bindings) {
26
31
  const expression = block.bindings[binding];
27
- const value = evaluate({ code: expression, state, context });
32
+ const value = evaluate({
33
+ code: expression,
34
+ localState,
35
+ rootState,
36
+ rootSetState,
37
+ context,
38
+ });
28
39
  set(copied, binding, value);
29
40
  }
30
41
 
@@ -35,7 +46,9 @@ export function getProcessedBlock({
35
46
  block,
36
47
  context,
37
48
  shouldEvaluateBindings,
38
- state,
49
+ localState,
50
+ rootState,
51
+ rootSetState,
39
52
  }: {
40
53
  block: BuilderBlock;
41
54
  /**
@@ -43,11 +56,20 @@ export function getProcessedBlock({
43
56
  * also sometimes too early to consider bindings, e.g. when we might be looking at a repeated block.
44
57
  */
45
58
  shouldEvaluateBindings: boolean;
46
- } & Pick<BuilderContextInterface, 'state' | 'context'>): BuilderBlock {
59
+ } & Pick<
60
+ BuilderContextInterface,
61
+ 'localState' | 'context' | 'rootState' | 'rootSetState'
62
+ >): BuilderBlock {
47
63
  const transformedBlock = transformBlock(block);
48
64
 
49
65
  if (shouldEvaluateBindings) {
50
- return evaluateBindings({ block: transformedBlock, state, context });
66
+ return evaluateBindings({
67
+ block: transformedBlock,
68
+ localState,
69
+ rootState,
70
+ rootSetState,
71
+ context,
72
+ });
51
73
  } else {
52
74
  return transformedBlock;
53
75
  }
@@ -1,22 +1,175 @@
1
1
  import type { CanTrack } from '../types/can-track.js';
2
- import { getCookie, setCookie } from './cookie.js';
2
+ import { getCookie, getCookieSync, setCookie } from './cookie.js';
3
+ import type {
4
+ BuilderContent,
5
+ BuilderContentVariation,
6
+ } from '../types/builder-content.js';
7
+ import type { Nullable, Overwrite } from '../types/typescript.js';
8
+ import { checkIsDefined } from '../helpers/nullable.js';
9
+ import { logger } from './logger.js';
3
10
 
4
- const BUILDER_STORE_PREFIX = 'builderio.variations';
11
+ const BUILDER_STORE_PREFIX = 'builder.tests';
5
12
 
6
13
  const getContentTestKey = (id: string) => `${BUILDER_STORE_PREFIX}.${id}`;
7
14
 
8
- export const getContentVariationCookie = ({
9
- contentId,
10
- canTrack,
11
- }: { contentId: string } & CanTrack) =>
12
- getCookie({ name: getContentTestKey(contentId), canTrack });
15
+ const getContentVariationCookie = ({ contentId }: { contentId: string }) =>
16
+ getCookie({ name: getContentTestKey(contentId), canTrack: true });
13
17
 
14
- export const setContentVariationCookie = ({
18
+ const getContentVariationCookieSync = ({ contentId }: { contentId: string }) =>
19
+ getCookieSync({ name: getContentTestKey(contentId), canTrack: true });
20
+
21
+ const setContentVariationCookie = ({
15
22
  contentId,
16
- canTrack,
17
23
  value,
18
24
  }: {
19
25
  contentId: string;
20
26
  value: string;
21
- } & CanTrack) =>
22
- setCookie({ name: getContentTestKey(contentId), value, canTrack });
27
+ }) => setCookie({ name: getContentTestKey(contentId), value, canTrack: true });
28
+
29
+ type BuilderContentWithVariations = Overwrite<
30
+ BuilderContent,
31
+ Required<Pick<BuilderContent, 'variations' | 'id'>>
32
+ >;
33
+
34
+ const checkIsBuilderContentWithVariations = (
35
+ item: BuilderContent
36
+ ): item is BuilderContentWithVariations =>
37
+ checkIsDefined(item.id) &&
38
+ checkIsDefined(item.variations) &&
39
+ Object.keys(item.variations).length > 0;
40
+
41
+ /**
42
+ * Randomly assign a variation to a user
43
+ */
44
+ const getRandomVariationId = ({
45
+ id,
46
+ variations,
47
+ }: BuilderContentWithVariations) => {
48
+ let n = 0;
49
+ const random = Math.random();
50
+
51
+ // loop over variations test ratios, incrementing a counter,
52
+ // until we find the variation that this user should be assigned to
53
+ for (const id in variations) {
54
+ const testRatio = variations[id]?.testRatio;
55
+ n += testRatio!;
56
+
57
+ if (random < n) {
58
+ return id;
59
+ }
60
+ }
61
+
62
+ // the variations array does not include the default variation.
63
+ // if we arrive here, then it means that the random number fits in the default variation bucket.
64
+ return id;
65
+ };
66
+
67
+ const getAndSetVariantId = (args: BuilderContentWithVariations) => {
68
+ // if variation not found in storage, assign a random variation to this user
69
+ const randomVariationId = getRandomVariationId(args);
70
+
71
+ // store variation in cookies/storage
72
+ setContentVariationCookie({
73
+ contentId: args.id,
74
+ value: randomVariationId,
75
+ }).catch((err) => {
76
+ logger.error('could not store A/B test variation: ', err);
77
+ });
78
+
79
+ return randomVariationId;
80
+ };
81
+
82
+ type TestFields = {
83
+ data?: BuilderContentVariation['data'];
84
+ testVariationId?: string;
85
+ testVariationName: string;
86
+ };
87
+
88
+ const getTestFields = ({
89
+ item,
90
+ testGroupId,
91
+ }: {
92
+ item: BuilderContentWithVariations;
93
+ testGroupId: string;
94
+ }): TestFields => {
95
+ const variationValue = item.variations[testGroupId];
96
+ if (
97
+ testGroupId === item.id ||
98
+ // handle edge-case where `testGroupId` points to non-existing variation
99
+ !variationValue
100
+ ) {
101
+ return {
102
+ testVariationId: item.id,
103
+ testVariationName: 'Default',
104
+ };
105
+ } else {
106
+ return {
107
+ data: variationValue.data,
108
+ testVariationId: variationValue.id,
109
+ testVariationName:
110
+ variationValue.name || (variationValue.id === item.id ? 'Default' : ''),
111
+ };
112
+ }
113
+ };
114
+
115
+ export const handleABTestingSync = ({
116
+ item,
117
+ canTrack,
118
+ }: { item: Nullable<BuilderContent> } & CanTrack): Nullable<BuilderContent> => {
119
+ if (!canTrack) {
120
+ return item;
121
+ }
122
+
123
+ if (!item) {
124
+ return undefined;
125
+ }
126
+
127
+ if (!checkIsBuilderContentWithVariations(item)) {
128
+ return item;
129
+ }
130
+
131
+ const testGroupId =
132
+ getContentVariationCookieSync({
133
+ contentId: item.id,
134
+ }) ||
135
+ getAndSetVariantId({
136
+ variations: item.variations,
137
+ id: item.id,
138
+ });
139
+
140
+ const variationValue = getTestFields({ item, testGroupId });
141
+ return {
142
+ ...item,
143
+ ...variationValue,
144
+ };
145
+ };
146
+
147
+ export const handleABTesting = async ({
148
+ item,
149
+ canTrack,
150
+ }: { item: BuilderContent } & CanTrack): Promise<BuilderContent> => {
151
+ if (!canTrack) {
152
+ return item;
153
+ }
154
+
155
+ if (!checkIsBuilderContentWithVariations(item)) {
156
+ return item;
157
+ }
158
+
159
+ const cookieValue = await getContentVariationCookie({
160
+ contentId: item.id,
161
+ });
162
+
163
+ const testGroupId =
164
+ cookieValue ||
165
+ getAndSetVariantId({
166
+ variations: item.variations,
167
+ id: item.id,
168
+ });
169
+
170
+ const variationValue = getTestFields({ item, testGroupId });
171
+ return {
172
+ ...item,
173
+ ...variationValue,
174
+ };
175
+ };
@@ -0,0 +1,4 @@
1
+ import { checkIsDefined } from './nullable';
2
+
3
+ export const getDefaultCanTrack = (canTrack?: boolean) =>
4
+ checkIsDefined(canTrack) ? canTrack : true;
@@ -1,17 +1,17 @@
1
1
  import { isBrowser } from '../functions/is-browser.js';
2
2
  import type { CanTrack } from '../types/can-track.js';
3
+ import { logger } from './logger.js';
3
4
  import { checkIsDefined } from './nullable.js';
4
5
  import { getTopLevelDomain } from './url.js';
5
6
 
6
- /**
7
- * NOTE: This function is `async` because its react-native override is async. Do not remove the `async` keyword!
8
- */
9
- export const getCookie = async ({
7
+ type GetCookieArgs = {
8
+ name: string;
9
+ } & CanTrack;
10
+
11
+ export const getCookieSync = ({
10
12
  name,
11
13
  canTrack,
12
- }: {
13
- name: string;
14
- } & CanTrack): Promise<string | undefined> => {
14
+ }: GetCookieArgs): string | undefined => {
15
15
  try {
16
16
  if (!canTrack) {
17
17
  return undefined;
@@ -25,11 +25,16 @@ export const getCookie = async ({
25
25
  .split('; ')
26
26
  .find((row) => row.startsWith(`${name}=`))
27
27
  ?.split('=')[1];
28
- } catch (err) {
29
- console.debug('[COOKIE] GET error: ', err);
28
+ } catch (err: any) {
29
+ logger.warn('[COOKIE] GET error: ', err?.message || err);
30
30
  return undefined;
31
31
  }
32
32
  };
33
+ /**
34
+ * NOTE: This function is `async` because its react-native override is async. Do not remove the `async` keyword!
35
+ * The sync version is only safe to use in code blocks that `react-native` is guaranteed not to not run.
36
+ */
37
+ export const getCookie = async (args: GetCookieArgs) => getCookieSync(args);
33
38
 
34
39
  type CookieConfiguration = Array<
35
40
  | ['expires', string]
@@ -101,7 +106,7 @@ export const setCookie = async ({
101
106
  }
102
107
  const cookie = createCookieString({ name, value, expires });
103
108
  document.cookie = cookie;
104
- } catch (err) {
105
- console.warn('[COOKIE] SET error: ', err);
109
+ } catch (err: any) {
110
+ logger.warn('[COOKIE] SET error: ', err?.message || err);
106
111
  }
107
112
  };
@@ -3,4 +3,5 @@ export const logger = {
3
3
  log: (...message: any[]) => console.log(MSG_PREFIX, ...message),
4
4
  error: (...message: any[]) => console.error(MSG_PREFIX, ...message),
5
5
  warn: (...message: any[]) => console.warn(MSG_PREFIX, ...message),
6
+ debug: (...message: any[]) => console.debug(MSG_PREFIX, ...message),
6
7
  };
@@ -3,8 +3,8 @@ export { default as Columns } from '../blocks/columns/columns.svelte';
3
3
  export { default as Fragment } from '../blocks/fragment/fragment.svelte';
4
4
  export { default as Image } from '../blocks/image/image.svelte';
5
5
  export { default as RenderBlocks } from '../components/render-blocks.svelte';
6
- export { default as RenderContent } from '../components/render-content/render-content.svelte';
7
6
  export { default as Section } from '../blocks/section/section.svelte';
8
7
  export { default as Symbol } from '../blocks/symbol/symbol.svelte';
9
8
  export { default as Text } from '../blocks/text/text.svelte';
10
9
  export { default as Video } from '../blocks/video/video.svelte';
10
+ export { default as RenderContent } from '../components/render-content-variants/render-content-variants.svelte';
package/src/index.ts CHANGED
@@ -1,15 +1,24 @@
1
1
  export * from './index-helpers/top-of-file.js';
2
-
3
2
  export * from './index-helpers/blocks-exports.js';
4
3
 
5
- export * from './functions/is-editing.js';
6
- export * from './functions/is-previewing.js';
7
- export * from './functions/register-component.js';
8
- export * from './functions/register.js';
9
- export * from './functions/set-editor-settings.js';
10
- export * from './functions/get-content/index.js';
11
- export * from './functions/get-builder-search-params/index.js';
4
+ export { isEditing } from './functions/is-editing.js';
5
+ export { isPreviewing } from './functions/is-previewing.js';
6
+ export { createRegisterComponentMessage } from './functions/register-component.js';
7
+
8
+ export { register } from './functions/register.js';
9
+ export type { InsertMenuConfig, InsertMenuItem } from './functions/register.js';
10
+
11
+ export { setEditorSettings } from './functions/set-editor-settings.js';
12
+ export type { Settings } from './functions/set-editor-settings.js';
13
+
14
+ export {
15
+ getAllContent,
16
+ getContent,
17
+ processContentResult,
18
+ } from './functions/get-content/index.js';
19
+
12
20
  export { track } from './functions/track/index.js';
21
+
13
22
  export type { RegisteredComponent } from './context/types';
14
23
  export type { ComponentInfo } from './types/components';
15
24
  export type { RenderContentProps } from './components/render-content/render-content.types.js';