@builder.io/sdk-svelte 0.0.3 → 0.0.5

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 (233) hide show
  1. package/package/--package.json +160 -0
  2. package/package/blocks/BaseText.svelte +19 -0
  3. package/package/blocks/BaseText.svelte.d.ts +19 -0
  4. package/package/blocks/columns/columns.svelte +13 -4
  5. package/package/blocks/columns/columns.svelte.d.ts +1 -1
  6. package/package/blocks/custom-code/custom-code.svelte +1 -1
  7. package/package/blocks/embed/embed.svelte +2 -0
  8. package/package/blocks/form/form.svelte +7 -5
  9. package/package/blocks/image/image.svelte +48 -46
  10. package/package/blocks/image/image.svelte.d.ts +1 -1
  11. package/package/blocks/img/img.svelte +7 -2
  12. package/package/blocks/section/section.svelte +7 -2
  13. package/package/blocks/symbol/symbol.svelte +13 -13
  14. package/package/blocks/symbol/symbol.svelte.d.ts +3 -3
  15. package/package/blocks/video/video.svelte +14 -3
  16. package/package/components/render-block/block-styles.svelte +24 -19
  17. package/package/components/render-block/block-styles.svelte.d.ts +2 -2
  18. package/package/components/render-block/render-block.svelte +81 -58
  19. package/package/components/render-block/render-block.svelte.d.ts +2 -2
  20. package/package/components/render-block/render-component-with-context.svelte +32 -0
  21. package/package/components/render-block/render-component-with-context.svelte.d.ts +19 -0
  22. package/package/components/render-block/render-component.svelte +1 -3
  23. package/package/components/render-block/render-component.svelte.d.ts +2 -2
  24. package/package/components/render-block/render-repeated-block.svelte +3 -0
  25. package/package/components/render-block/types.d.ts +2 -2
  26. package/package/components/render-block/types.js +1 -2
  27. package/package/components/render-blocks.svelte +12 -1
  28. package/package/components/render-blocks.svelte.d.ts +3 -1
  29. package/package/components/render-content/components/render-styles.svelte +2 -1
  30. package/package/components/render-content/render-content.svelte +24 -8
  31. package/package/components/render-content/render-content.svelte.d.ts +2 -2
  32. package/package/constants/builder-registered-components.js +3 -0
  33. package/package/context/builder.context.d.ts +1 -0
  34. package/package/context/builder.context.js +1 -0
  35. package/package/context/types.d.ts +4 -3
  36. package/package/context/types.js +1 -3
  37. package/package/functions/extract-text-styles.d.ts +4 -0
  38. package/package/functions/extract-text-styles.js +34 -0
  39. package/package/functions/fast-clone.d.ts +4 -0
  40. package/package/functions/fast-clone.js +4 -0
  41. package/package/functions/get-block-actions-handler.d.ts +5 -0
  42. package/package/functions/get-block-actions-handler.js +9 -0
  43. package/package/functions/get-block-actions.d.ts +1 -1
  44. package/package/functions/get-block-actions.js +2 -8
  45. package/package/functions/get-block-component-options.d.ts +1 -1
  46. package/package/functions/get-block-component-options.js +0 -1
  47. package/package/functions/get-block-properties.d.ts +1 -1
  48. package/package/functions/get-block-properties.js +0 -1
  49. package/package/functions/get-block-tag.d.ts +1 -1
  50. package/package/functions/get-block-tag.js +0 -1
  51. package/package/functions/get-content/ab-testing.d.ts +5 -3
  52. package/package/functions/get-content/ab-testing.js +75 -37
  53. package/package/functions/get-content/index.d.ts +1 -1
  54. package/package/functions/get-content/index.js +4 -5
  55. package/package/functions/get-content/types.d.ts +7 -2
  56. package/package/functions/get-processed-block.d.ts +1 -1
  57. package/package/functions/get-processed-block.js +5 -4
  58. package/package/functions/get-processed-block.test.js +0 -1
  59. package/package/functions/get-react-native-block-styles.d.ts +6 -0
  60. package/package/functions/get-react-native-block-styles.js +16 -0
  61. package/package/functions/register-component.d.ts +2 -2
  62. package/package/functions/register-component.js +1 -5
  63. package/package/functions/register.d.ts +2 -2
  64. package/package/functions/register.js +0 -2
  65. package/package/functions/sanitize-react-native-block-styles.d.ts +3 -0
  66. package/package/functions/sanitize-react-native-block-styles.js +53 -0
  67. package/package/functions/track.d.ts +1 -1
  68. package/package/functions/track.js +5 -6
  69. package/package/functions/transform-block.d.ts +1 -1
  70. package/package/functions/transform-block.js +0 -1
  71. package/package/helpers/ab-tests.d.ts +8 -0
  72. package/package/helpers/ab-tests.js +5 -0
  73. package/package/helpers/cookie.d.ts +9 -3
  74. package/package/helpers/cookie.js +8 -3
  75. package/package/helpers/css.d.ts +5 -1
  76. package/package/helpers/css.js +14 -1
  77. package/package/helpers/localStorage.d.ts +3 -4
  78. package/package/helpers/sessionId.d.ts +3 -4
  79. package/package/helpers/sessionId.js +5 -3
  80. package/package/helpers/visitorId.d.ts +3 -5
  81. package/package/scripts/init-editing.js +42 -38
  82. package/package/types/builder-content.d.ts +1 -1
  83. package/package/types/builder-content.js +1 -1
  84. package/package/types/targets.d.ts +1 -1
  85. package/package/types/typescript.d.ts +1 -0
  86. package/package.json +10 -4
  87. package/src/blocks/BaseText.svelte +27 -0
  88. package/{package/blocks/button/button.lite.tsx → src/blocks/button/button.svelte} +0 -0
  89. package/src/blocks/button/component-info.ts +42 -0
  90. package/{package/blocks/columns/columns.lite.tsx → src/blocks/columns/columns.svelte} +26 -17
  91. package/src/blocks/columns/component-info.ts +254 -0
  92. package/src/blocks/custom-code/component-info.ts +31 -0
  93. package/{package/blocks/custom-code/custom-code.lite.tsx → src/blocks/custom-code/custom-code.svelte} +1 -5
  94. package/src/blocks/embed/component-info.ts +53 -0
  95. package/{package/blocks/embed/embed.lite.tsx → src/blocks/embed/embed.svelte} +1 -2
  96. package/src/blocks/embed/helpers.ts +8 -0
  97. package/src/blocks/form/component-info.ts +282 -0
  98. package/{package/blocks/form/form.lite.tsx → src/blocks/form/form.svelte} +12 -31
  99. package/src/blocks/fragment/component-info.ts +10 -0
  100. package/{package/blocks/fragment/fragment.lite.tsx → src/blocks/fragment/fragment.svelte} +0 -0
  101. package/src/blocks/image/component-info.ts +170 -0
  102. package/src/blocks/image/image.helpers.ts +75 -0
  103. package/{package/blocks/image/image.lite.tsx → src/blocks/image/image.svelte} +47 -50
  104. package/src/blocks/img/component-info.ts +21 -0
  105. package/{package/blocks/img/img.lite.tsx → src/blocks/img/img.svelte} +6 -3
  106. package/src/blocks/input/component-info.ts +78 -0
  107. package/{package/blocks/input/input.lite.tsx → src/blocks/input/input.svelte} +0 -0
  108. package/src/blocks/raw-text/component-info.ts +15 -0
  109. package/{package/blocks/raw-text/raw-text.lite.tsx → src/blocks/raw-text/raw-text.svelte} +2 -1
  110. package/src/blocks/section/component-info.ts +50 -0
  111. package/{package/blocks/section/section.lite.tsx → src/blocks/section/section.svelte} +6 -2
  112. package/src/blocks/select/component-info.ts +61 -0
  113. package/{package/blocks/select/select.lite.tsx → src/blocks/select/select.svelte} +0 -0
  114. package/src/blocks/submit-button/component-info.ts +32 -0
  115. package/{package/blocks/submit-button/submit-button.lite.tsx → src/blocks/submit-button/submit-button.svelte} +0 -0
  116. package/src/blocks/symbol/component-info.ts +43 -0
  117. package/{package/blocks/symbol/symbol.lite.tsx → src/blocks/symbol/symbol.svelte} +23 -15
  118. package/src/blocks/text/component-info.ts +24 -0
  119. package/{package/blocks/text/text.lite.tsx → src/blocks/text/text.svelte} +0 -0
  120. package/src/blocks/textarea/component-info.ts +48 -0
  121. package/{package/blocks/textarea/textarea.lite.tsx → src/blocks/textarea/textarea.svelte} +0 -0
  122. package/src/blocks/util.ts +6 -0
  123. package/src/blocks/video/component-info.ts +109 -0
  124. package/{package/blocks/video/video.lite.tsx → src/blocks/video/video.svelte} +13 -3
  125. package/{package/components/render-block/block-styles.lite.tsx → src/components/render-block/block-styles.svelte} +28 -26
  126. package/src/components/render-block/render-block.helpers.ts +27 -0
  127. package/{package/components/render-block/render-block.lite.tsx → src/components/render-block/render-block.svelte} +93 -81
  128. package/src/components/render-block/render-component-with-context.svelte +38 -0
  129. package/{package/components/render-block/render-component.lite.tsx → src/components/render-block/render-component.svelte} +5 -3
  130. package/{package/components/render-block/render-repeated-block.lite.tsx → src/components/render-block/render-repeated-block.svelte} +4 -0
  131. package/src/components/render-block/types.ts +7 -0
  132. package/{package/components/render-blocks.lite.tsx → src/components/render-blocks.svelte} +15 -2
  133. package/{package/components/render-content/components/render-styles.lite.tsx → src/components/render-content/components/render-styles.svelte} +2 -8
  134. package/src/components/render-content/index.ts +1 -0
  135. package/{package/components/render-content/render-content.lite.tsx → src/components/render-content/render-content.svelte} +34 -36
  136. package/{package/components/render-inlined-styles.lite.tsx → src/components/render-inlined-styles.svelte} +0 -1
  137. package/src/constants/builder-registered-components.ts +42 -0
  138. package/src/constants/device-sizes.ts +28 -0
  139. package/src/constants/target.ts +2 -0
  140. package/src/context/builder.context.ts +13 -0
  141. package/src/context/types.ts +23 -0
  142. package/src/functions/camel-to-kebab-case.ts +2 -0
  143. package/src/functions/evaluate.ts +52 -0
  144. package/src/functions/event-handler-name.ts +6 -0
  145. package/src/functions/extract-text-styles.ts +39 -0
  146. package/src/functions/fast-clone.ts +5 -0
  147. package/src/functions/get-block-actions-handler.ts +19 -0
  148. package/src/functions/get-block-actions.ts +25 -0
  149. package/src/functions/get-block-component-options.ts +12 -0
  150. package/src/functions/get-block-properties.ts +11 -0
  151. package/src/functions/get-block-tag.ts +7 -0
  152. package/src/functions/get-builder-search-params/fn.test.ts +19 -0
  153. package/src/functions/get-builder-search-params/index.ts +48 -0
  154. package/src/functions/get-content/ab-testing.ts +132 -0
  155. package/src/functions/get-content/fn.test.ts +35 -0
  156. package/src/functions/get-content/index.ts +77 -0
  157. package/src/functions/get-content/types.ts +36 -0
  158. package/src/functions/get-fetch.ts +14 -0
  159. package/src/functions/get-global-this.ts +15 -0
  160. package/src/functions/get-processed-block.test.ts +33 -0
  161. package/src/functions/get-processed-block.ts +54 -0
  162. package/src/functions/get-react-native-block-styles.ts +30 -0
  163. package/src/functions/if-target.ts +20 -0
  164. package/src/functions/is-browser.ts +3 -0
  165. package/src/functions/is-editing.ts +7 -0
  166. package/src/functions/is-iframe.ts +5 -0
  167. package/src/functions/is-previewing.ts +14 -0
  168. package/src/functions/mark-mutable.ts +7 -0
  169. package/src/functions/on-change.test.ts +23 -0
  170. package/src/functions/on-change.ts +29 -0
  171. package/src/functions/register-component.ts +67 -0
  172. package/src/functions/register.ts +46 -0
  173. package/src/functions/sanitize-react-native-block-styles.ts +65 -0
  174. package/src/functions/set-editor-settings.ts +16 -0
  175. package/src/functions/set.test.ts +19 -0
  176. package/src/functions/set.ts +26 -0
  177. package/src/functions/track.ts +90 -0
  178. package/src/functions/transform-block.ts +6 -0
  179. package/src/helpers/ab-tests.ts +22 -0
  180. package/src/helpers/cookie.ts +102 -0
  181. package/src/helpers/css.ts +33 -0
  182. package/src/helpers/flatten.ts +24 -0
  183. package/src/helpers/localStorage.ts +38 -0
  184. package/src/helpers/nullable.ts +4 -0
  185. package/src/helpers/sessionId.ts +34 -0
  186. package/src/helpers/time.ts +4 -0
  187. package/src/helpers/url.test.ts +16 -0
  188. package/src/helpers/url.ts +12 -0
  189. package/src/helpers/uuid.ts +17 -0
  190. package/src/helpers/visitorId.ts +38 -0
  191. package/src/index-helpers/blocks-exports.ts +10 -0
  192. package/src/index-helpers/top-of-file.ts +4 -0
  193. package/src/index.ts +12 -0
  194. package/src/scripts/init-editing.ts +102 -0
  195. package/src/types/builder-block.ts +58 -0
  196. package/src/types/builder-content.ts +39 -0
  197. package/src/types/can-track.ts +3 -0
  198. package/src/types/components.ts +188 -0
  199. package/src/types/deep-partial.ts +7 -0
  200. package/src/types/element.ts +60 -0
  201. package/src/types/targets.ts +6 -0
  202. package/src/types/typescript.ts +7 -0
  203. package/package/blocks/button/button.lite.d.ts +0 -0
  204. package/package/blocks/columns/columns.lite.d.ts +0 -0
  205. package/package/blocks/custom-code/custom-code.lite.d.ts +0 -0
  206. package/package/blocks/embed/embed.lite.d.ts +0 -0
  207. package/package/blocks/form/form.lite.d.ts +0 -0
  208. package/package/blocks/fragment/fragment.lite.d.ts +0 -0
  209. package/package/blocks/image/image.lite.d.ts +0 -0
  210. package/package/blocks/img/img.lite.d.ts +0 -0
  211. package/package/blocks/input/input.lite.d.ts +0 -0
  212. package/package/blocks/raw-text/raw-text.lite.d.ts +0 -0
  213. package/package/blocks/section/section.lite.d.ts +0 -0
  214. package/package/blocks/select/select.lite.d.ts +0 -0
  215. package/package/blocks/submit-button/submit-button.lite.d.ts +0 -0
  216. package/package/blocks/symbol/symbol.lite.d.ts +0 -0
  217. package/package/blocks/text/text.lite.d.ts +0 -0
  218. package/package/blocks/textarea/textarea.lite.d.ts +0 -0
  219. package/package/blocks/video/video.lite.d.ts +0 -0
  220. package/package/components/render-block/block-styles.lite.d.ts +0 -0
  221. package/package/components/render-block/render-block.lite.d.ts +0 -0
  222. package/package/components/render-block/render-component.lite.d.ts +0 -0
  223. package/package/components/render-block/render-repeated-block.lite.d.ts +0 -0
  224. package/package/components/render-blocks.lite.d.ts +0 -0
  225. package/package/components/render-content/components/render-styles.lite.d.ts +0 -0
  226. package/package/components/render-content/render-content.lite.d.ts +0 -0
  227. package/package/components/render-inlined-styles.lite.d.ts +0 -0
  228. package/package/functions/convert-style-object.d.ts +0 -1
  229. package/package/functions/convert-style-object.js +0 -3
  230. package/package/functions/get-block-styles.d.ts +0 -2
  231. package/package/functions/get-block-styles.js +0 -21
  232. package/package/functions/sanitize-styles.d.ts +0 -4
  233. package/package/functions/sanitize-styles.js +0 -6
@@ -12,7 +12,6 @@
12
12
  $: injectedStyleScript = () => {
13
13
  return `<${tagName()}>${styles}</${tagName()}>`;
14
14
  };
15
-
16
15
  $: tagName = () => {
17
16
  // NOTE: we have to obfusctate the name of the tag due to a limitation in the svelte-preprocessor plugin.
18
17
  // https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
@@ -0,0 +1,42 @@
1
+ import { default as Button } from '../blocks/button/button.svelte';
2
+ import { componentInfo as buttonComponentInfo } from '../blocks/button/component-info.js';
3
+ import { default as Columns } from '../blocks/columns/columns.svelte';
4
+ import { componentInfo as columnsComponentInfo } from '../blocks/columns/component-info.js';
5
+ import { componentInfo as fragmentComponentInfo } from '../blocks/fragment/component-info.js';
6
+ import { default as Fragment } from '../blocks/fragment/fragment.svelte';
7
+ import { componentInfo as imageComponentInfo } from '../blocks/image/component-info.js';
8
+ import { default as Image } from '../blocks/image/image.svelte';
9
+ import { componentInfo as sectionComponentInfo } from '../blocks/section/component-info.js';
10
+ import { default as Section } from '../blocks/section/section.svelte';
11
+ import { componentInfo as symbolComponentInfo } from '../blocks/symbol/component-info.js';
12
+ import { default as Symbol } from '../blocks/symbol/symbol.svelte';
13
+ import { componentInfo as textComponentInfo } from '../blocks/text/component-info.js';
14
+ import { default as Text } from '../blocks/text/text.svelte';
15
+ import { componentInfo as videoComponentInfo } from '../blocks/video/component-info.js';
16
+ import { default as Video } from '../blocks/video/video.svelte';
17
+ import type { RegisteredComponent } from '../context/types.js';
18
+ import { componentInfo as embedComponentInfo } from '../blocks/embed/component-info.js';
19
+ import { default as embed } from '../blocks/embed/embed.svelte';
20
+ import { default as Img } from '../blocks/img/img.svelte';
21
+ import { componentInfo as imgComponentInfo } from '../blocks/img/component-info.js';
22
+ import { default as customCode } from '../blocks/custom-code/custom-code.svelte';
23
+ import { componentInfo as customCodeInfo } from '../blocks/custom-code/component-info.js';
24
+
25
+ /**
26
+ * Returns a list of all registered components.
27
+ * NOTE: This needs to be a function to work around ESM circular dependencies.
28
+ */
29
+ export const getDefaultRegisteredComponents: () => RegisteredComponent[] =
30
+ () => [
31
+ { component: Columns, ...columnsComponentInfo },
32
+ { component: Image, ...imageComponentInfo },
33
+ { component: Img, ...imgComponentInfo },
34
+ { component: Text, ...textComponentInfo },
35
+ { component: Video, ...videoComponentInfo },
36
+ { component: Symbol, ...symbolComponentInfo },
37
+ { component: Button, ...buttonComponentInfo },
38
+ { component: Section, ...sectionComponentInfo },
39
+ { component: Fragment, ...fragmentComponentInfo },
40
+ { component: embed, ...embedComponentInfo },
41
+ { component: customCode, ...customCodeInfo },
42
+ ];
@@ -0,0 +1,28 @@
1
+ export type SizeName = 'large' | 'medium' | 'small';
2
+
3
+ interface Size {
4
+ min: number;
5
+ default: number;
6
+ max: number;
7
+ }
8
+
9
+ const SIZES: Record<SizeName, Size> = {
10
+ small: {
11
+ min: 320,
12
+ default: 321,
13
+ max: 640,
14
+ },
15
+ medium: {
16
+ min: 641,
17
+ default: 642,
18
+ max: 991,
19
+ },
20
+ large: {
21
+ min: 990,
22
+ default: 991,
23
+ max: 1200,
24
+ },
25
+ };
26
+
27
+ export const getMaxWidthQueryForSize = (size: SizeName) =>
28
+ `@media (max-width: ${SIZES[size].max}px)`;
@@ -0,0 +1,2 @@
1
+ /** This file should be overriden for each framework. Ideally this would be implemented in Mitosis. */
2
+ export const TARGET = 'svelte';
@@ -0,0 +1,13 @@
1
+ const key = Symbol();
2
+
3
+ export default {
4
+ Builder: {
5
+ content: null,
6
+ context: {},
7
+ state: {},
8
+ apiKey: null,
9
+ registeredComponents: {},
10
+ inheritedStyles: {},
11
+ },
12
+ key,
13
+ };
@@ -0,0 +1,23 @@
1
+ import type { BuilderContent } from '../types/builder-content.js';
2
+ import type { ComponentInfo } from '../types/components.js';
3
+ import type { Dictionary, Nullable } from '../types/typescript.js';
4
+
5
+ export type RegisteredComponent = ComponentInfo & {
6
+ component: any;
7
+ };
8
+
9
+ export type RegisteredComponents = Dictionary<RegisteredComponent>;
10
+
11
+ export type BuilderRenderState = Record<string, unknown>;
12
+
13
+ export type BuilderRenderContext = Record<string, unknown>;
14
+
15
+ export interface BuilderContextInterface {
16
+ content: Nullable<BuilderContent>;
17
+ context: BuilderRenderContext;
18
+ state: BuilderRenderState;
19
+ apiKey: string | null;
20
+ registeredComponents: RegisteredComponents;
21
+ // Used to recursively store all CSS coming from a parent that would apply to a Text block
22
+ inheritedStyles: Record<string, unknown>;
23
+ }
@@ -0,0 +1,2 @@
1
+ export const camelToKebabCase = (string: string) =>
2
+ string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
@@ -0,0 +1,52 @@
1
+ import type { BuilderContextInterface } from '../context/types.js';
2
+ import { isBrowser } from './is-browser.js';
3
+ import { isEditing } from './is-editing.js';
4
+
5
+ export function evaluate({
6
+ code,
7
+ context,
8
+ state,
9
+ event,
10
+ }: {
11
+ code: string;
12
+ event?: Event;
13
+ } & Pick<BuilderContextInterface, 'state' | 'context'>): any {
14
+ if (code === '') {
15
+ console.warn('Skipping evaluation of empty code block.');
16
+ return;
17
+ }
18
+
19
+ const builder = {
20
+ isEditing: isEditing(),
21
+ isBrowser: isBrowser(),
22
+ isServer: !isBrowser(),
23
+ };
24
+
25
+ // Be able to handle simple expressions like "state.foo" or "1 + 1"
26
+ // as well as full blocks like "var foo = "bar"; return foo"
27
+ const useReturn = !(
28
+ code.includes(';') ||
29
+ code.includes(' return ') ||
30
+ code.trim().startsWith('return ')
31
+ );
32
+
33
+ const useCode = useReturn ? `return (${code});` : code;
34
+
35
+ try {
36
+ return new Function(
37
+ 'builder',
38
+ 'Builder' /* <- legacy */,
39
+ 'state',
40
+ 'context',
41
+ 'event',
42
+ useCode
43
+ )(builder, builder, state, context, event);
44
+ } catch (e) {
45
+ console.warn(
46
+ 'Builder custom code error: \n While Evaluating: \n ',
47
+ useCode,
48
+ '\n',
49
+ (e as any).message || e
50
+ );
51
+ }
52
+ }
@@ -0,0 +1,6 @@
1
+ function capitalizeFirstLetter(string: string) {
2
+ return string.charAt(0).toUpperCase() + string.slice(1);
3
+ }
4
+
5
+ export const getEventHandlerName = (key: string) =>
6
+ `on${capitalizeFirstLetter(key)}`;
@@ -0,0 +1,39 @@
1
+ const TEXT_STYLE_KEYS = [
2
+ 'color',
3
+ 'whiteSpace',
4
+ 'direction',
5
+ 'hyphens',
6
+ 'overflowWrap',
7
+ ];
8
+
9
+ /**
10
+ * Check if the key represent a CSS style property that applies to text
11
+ * See MDN docs for refrence of what properties apply to text.
12
+ * https://developer.mozilla.org/en-US/docs/Learn/CSS/Styling_text/Fundamentals#summary
13
+ */
14
+ const isTextStyle = (key: string) => {
15
+ return (
16
+ TEXT_STYLE_KEYS.includes(key) ||
17
+ key.startsWith('font') ||
18
+ key.startsWith('text') ||
19
+ key.startsWith('letter') ||
20
+ key.startsWith('line') ||
21
+ key.startsWith('word') ||
22
+ key.startsWith('writing')
23
+ );
24
+ };
25
+
26
+ /**
27
+ * Extract styles that apply to text from a style object.
28
+ */
29
+ export const extractTextStyles = (styles: Partial<CSSStyleDeclaration>) => {
30
+ const textStyles: Partial<CSSStyleDeclaration> = {};
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ Object.entries(styles).forEach(([key, value]: [any, any]) => {
33
+ if (isTextStyle(key)) {
34
+ textStyles[key] = value;
35
+ }
36
+ });
37
+
38
+ return textStyles;
39
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * We need to serialize values to a string in case there are Proxy values, as is the case with SolidJS etc.
3
+ */
4
+ export const fastClone = <T extends object>(obj: T): T =>
5
+ JSON.parse(JSON.stringify(obj));
@@ -0,0 +1,19 @@
1
+ import type { BuilderContextInterface } from '../context/types.js';
2
+ import type { BuilderBlock } from '../types/builder-block.js';
3
+ import { evaluate } from './evaluate.js';
4
+
5
+ export function crateEventHandler(
6
+ value: string,
7
+ options: { block: BuilderBlock } & Pick<
8
+ BuilderContextInterface,
9
+ 'state' | 'context'
10
+ >
11
+ ): (event: Event) => any {
12
+ return (event: Event) =>
13
+ evaluate({
14
+ code: value,
15
+ context: options.context,
16
+ state: options.state,
17
+ event,
18
+ });
19
+ }
@@ -0,0 +1,25 @@
1
+ import type { BuilderContextInterface } from '../context/types.js';
2
+ import type { BuilderBlock } from '../types/builder-block.js';
3
+ import { getEventHandlerName } from './event-handler-name.js';
4
+ import { crateEventHandler } from './get-block-actions-handler.js';
5
+
6
+ type Actions = { [index: string]: (event: Event) => any };
7
+
8
+ export function getBlockActions(
9
+ options: {
10
+ block: BuilderBlock;
11
+ } & Pick<BuilderContextInterface, 'state' | 'context'>
12
+ ): Actions {
13
+ const obj: Actions = {};
14
+ const optionActions = options.block.actions ?? {};
15
+
16
+ for (const key in optionActions) {
17
+ // eslint-disable-next-line no-prototype-builtins
18
+ if (!optionActions.hasOwnProperty(key)) {
19
+ continue;
20
+ }
21
+ const value = optionActions[key];
22
+ obj[getEventHandlerName(key)] = crateEventHandler(value, options);
23
+ }
24
+ return obj;
25
+ }
@@ -0,0 +1,12 @@
1
+ import type { BuilderBlock } from '../types/builder-block.js';
2
+
3
+ export function getBlockComponentOptions(block: BuilderBlock) {
4
+ return {
5
+ ...block.component?.options,
6
+ ...(block as any).options,
7
+ /**
8
+ * Our built-in components frequently make use of the block, so we provide all of it under `builderBlock`
9
+ */
10
+ builderBlock: block,
11
+ };
12
+ }
@@ -0,0 +1,11 @@
1
+ import type { BuilderBlock } from '../types/builder-block.js';
2
+
3
+ export function getBlockProperties(block: BuilderBlock) {
4
+ return {
5
+ ...block.properties,
6
+ 'builder-id': block.id,
7
+ class: [block.id, 'builder-block', block.class, block.properties?.class]
8
+ .filter(Boolean)
9
+ .join(' '),
10
+ };
11
+ }
@@ -0,0 +1,7 @@
1
+ import type { BuilderBlock } from '../types/builder-block.js';
2
+
3
+ export type TagName = string;
4
+
5
+ export function getBlockTag(block: BuilderBlock): TagName {
6
+ return block.tagName || 'div';
7
+ }
@@ -0,0 +1,19 @@
1
+ import { convertSearchParamsToQueryObject, getBuilderSearchParams } from '.';
2
+
3
+ const querystring =
4
+ 'someotherValue=jklsjfdal&abc=klfdjklgfds&builder.cachebust=true&builder.preview=page&builder.noCache=true&__builder_editing__=true&builder.overrides.page=037948e52eaf4743afed464f02c70da4&builder.overrides.037948e52eaf4743afed464f02c70da4=037948e52eaf4743afed464f02c70da4&builder.overrides.page%3A%2F=037948e52eaf4743afed464f02c70da4&preview_theme_id=128854393017';
5
+
6
+ const url = new URL(`localhost:3000/about-us?${querystring}`);
7
+
8
+ describe('Get Builder SearchParams', () => {
9
+ test('correctly converts URLSearchParams to object', () => {
10
+ const output = convertSearchParamsToQueryObject(url.searchParams);
11
+ expect(output).toMatchSnapshot();
12
+ });
13
+ test('correctly extracts all builder params from a query object', () => {
14
+ const output = getBuilderSearchParams(
15
+ convertSearchParamsToQueryObject(url.searchParams)
16
+ );
17
+ expect(output).toMatchSnapshot();
18
+ });
19
+ });
@@ -0,0 +1,48 @@
1
+ import { isBrowser } from '../is-browser.js';
2
+
3
+ const BUILDER_SEARCHPARAMS_PREFIX = 'builder.';
4
+
5
+ type QueryObject = Record<string, string>;
6
+
7
+ export const convertSearchParamsToQueryObject = (
8
+ searchParams: URLSearchParams
9
+ ): QueryObject => {
10
+ const options: Record<string, string> = {};
11
+ searchParams.forEach((value, key) => {
12
+ options[key] = value;
13
+ });
14
+ return options;
15
+ };
16
+
17
+ export const getBuilderSearchParams = (
18
+ _options: QueryObject | URLSearchParams | undefined
19
+ ) => {
20
+ if (!_options) {
21
+ return {};
22
+ }
23
+ const options = normalizeSearchParams(_options);
24
+
25
+ const newOptions: QueryObject = {};
26
+ Object.keys(options).forEach((key) => {
27
+ if (key.startsWith(BUILDER_SEARCHPARAMS_PREFIX)) {
28
+ const trimmedKey = key.replace(BUILDER_SEARCHPARAMS_PREFIX, '');
29
+ newOptions[trimmedKey] = options[key];
30
+ }
31
+ });
32
+ return newOptions;
33
+ };
34
+
35
+ export const getBuilderSearchParamsFromWindow = () => {
36
+ if (!isBrowser()) {
37
+ return {};
38
+ }
39
+ const searchParams = new URLSearchParams(window.location.search);
40
+ return getBuilderSearchParams(searchParams);
41
+ };
42
+
43
+ export const normalizeSearchParams = (
44
+ searchParams: QueryObject | URLSearchParams
45
+ ): QueryObject =>
46
+ searchParams instanceof URLSearchParams
47
+ ? convertSearchParamsToQueryObject(searchParams)
48
+ : searchParams;
@@ -0,0 +1,132 @@
1
+ import type {
2
+ BuilderContent,
3
+ BuilderContentVariation,
4
+ } from '../../types/builder-content.js';
5
+ import {
6
+ getContentVariationCookie,
7
+ setContentVariationCookie,
8
+ } from '../../helpers/ab-tests.js';
9
+ import type { Overwrite } from '../../types/typescript.js';
10
+ import type { CanTrack } from '../../types/can-track.js';
11
+ import { checkIsDefined } from '../../helpers/nullable.js';
12
+
13
+ type BuilderContentWithVariations = Overwrite<
14
+ BuilderContent,
15
+ Required<Pick<BuilderContent, 'variations' | 'id'>>
16
+ >;
17
+
18
+ const checkIsBuilderContentWithVariations = (
19
+ item: BuilderContent
20
+ ): item is BuilderContentWithVariations =>
21
+ checkIsDefined(item.id) &&
22
+ checkIsDefined(item.variations) &&
23
+ Object.keys(item.variations).length > 0;
24
+
25
+ /**
26
+ * Randomly assign a variation to this user and store it in cookies/storage
27
+ */
28
+ const getRandomVariationId = ({
29
+ id,
30
+ variations,
31
+ }: BuilderContentWithVariations) => {
32
+ let n = 0;
33
+ const random = Math.random();
34
+
35
+ // loop over variations test ratios, incrementing a counter,
36
+ // until we find the variation that this user should be assigned to
37
+ for (const id in variations) {
38
+ const testRatio = variations[id]?.testRatio;
39
+ n += testRatio!;
40
+
41
+ if (random < n) {
42
+ return id;
43
+ }
44
+ }
45
+
46
+ // `item.variations` does not include the default variation
47
+ // if we arrive here, then it means that the random number fits in the default variation bucket
48
+ return id;
49
+ };
50
+
51
+ const getTestFields = ({
52
+ item,
53
+ testGroupId,
54
+ }: {
55
+ item: BuilderContentWithVariations;
56
+ testGroupId: string;
57
+ }): TestFields => {
58
+ const variationValue = item.variations[testGroupId];
59
+ if (
60
+ testGroupId === item.id ||
61
+ // handle edge-case where `testGroupId` points to non-existing variation
62
+ !variationValue
63
+ ) {
64
+ return {
65
+ testVariationId: item.id,
66
+ testVariationName: 'Default',
67
+ };
68
+ } else {
69
+ return {
70
+ data: variationValue.data,
71
+ testVariationId: variationValue.id,
72
+ testVariationName:
73
+ variationValue.name || (variationValue.id === item.id ? 'Default' : ''),
74
+ };
75
+ }
76
+ };
77
+
78
+ type TestFields = {
79
+ data?: BuilderContentVariation['data'];
80
+ testVariationId?: string;
81
+ testVariationName: string;
82
+ };
83
+
84
+ const getContentVariation = async ({
85
+ item,
86
+ canTrack,
87
+ }: {
88
+ item: BuilderContentWithVariations;
89
+ } & CanTrack): Promise<TestFields> => {
90
+ // try to find test variation in cookies/storage
91
+ const testGroupId = await getContentVariationCookie({
92
+ canTrack,
93
+ contentId: item.id,
94
+ });
95
+
96
+ const testFields = testGroupId
97
+ ? getTestFields({ item, testGroupId })
98
+ : undefined;
99
+
100
+ if (testFields) {
101
+ return testFields;
102
+ } else {
103
+ // if variation not found in storage, assign a random variation to this user
104
+ const randomVariationId = getRandomVariationId({
105
+ variations: item.variations,
106
+ id: item.id,
107
+ });
108
+
109
+ // store variation in cookies/storage
110
+ setContentVariationCookie({
111
+ contentId: item.id,
112
+ value: randomVariationId,
113
+ canTrack,
114
+ }).catch((err) => {
115
+ console.error('could not store A/B test variation: ', err);
116
+ });
117
+
118
+ return getTestFields({ item, testGroupId: randomVariationId });
119
+ }
120
+ };
121
+
122
+ export const handleABTesting = async ({
123
+ item,
124
+ canTrack,
125
+ }: { item: BuilderContent } & CanTrack) => {
126
+ if (!checkIsBuilderContentWithVariations(item)) {
127
+ return;
128
+ }
129
+
130
+ const variationValue = await getContentVariation({ item, canTrack });
131
+ Object.assign(item, variationValue);
132
+ };
@@ -0,0 +1,35 @@
1
+ import { generateContentUrl } from '.';
2
+
3
+ const testKey = 'YJIGb4i01jvw0SRdL5Bt';
4
+ const testModel = 'page';
5
+ const testId = 'c1b81bab59704599b997574eb0736def';
6
+
7
+ const options = {
8
+ cachebust: 'true',
9
+ noCache: 'true',
10
+ 'overrides.037948e52eaf4743afed464f02c70da4':
11
+ '037948e52eaf4743afed464f02c70da4',
12
+ 'overrides.page': '037948e52eaf4743afed464f02c70da4',
13
+ 'overrides.page:/': '037948e52eaf4743afed464f02c70da4',
14
+ preview: 'page',
15
+ };
16
+
17
+ describe('Generate Content URL', () => {
18
+ test('generates the proper value for a simple query', () => {
19
+ const output = generateContentUrl({
20
+ apiKey: testKey,
21
+ model: testModel,
22
+ query: { id: testId },
23
+ });
24
+ expect(output).toMatchSnapshot();
25
+ });
26
+ test('Handles overrides correctly', () => {
27
+ const output = generateContentUrl({
28
+ apiKey: testKey,
29
+ model: testModel,
30
+ query: { id: testId },
31
+ options,
32
+ });
33
+ expect(output).toMatchSnapshot();
34
+ });
35
+ });
@@ -0,0 +1,77 @@
1
+ import { flatten } from '../../helpers/flatten.js';
2
+ import type { BuilderContent } from '../../types/builder-content.js';
3
+ import {
4
+ getBuilderSearchParamsFromWindow,
5
+ normalizeSearchParams,
6
+ } from '../get-builder-search-params/index.js';
7
+ import { getFetch } from '../get-fetch.js';
8
+ import { handleABTesting } from './ab-testing.js';
9
+
10
+ export type GetContentOptions = import('./types.js').GetContentOptions;
11
+
12
+ export async function getContent(
13
+ options: GetContentOptions
14
+ ): Promise<BuilderContent | null> {
15
+ return (await getAllContent({ ...options, limit: 1 })).results[0] || null;
16
+ }
17
+
18
+ export const generateContentUrl = (options: GetContentOptions): URL => {
19
+ const {
20
+ limit = 30,
21
+ userAttributes,
22
+ query,
23
+ noTraverse = false,
24
+ model,
25
+ apiKey,
26
+ } = options;
27
+
28
+ const url = new URL(
29
+ `https://cdn.builder.io/api/v2/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}`
30
+ );
31
+
32
+ const queryOptions = {
33
+ ...getBuilderSearchParamsFromWindow(),
34
+ ...normalizeSearchParams(options.options || {}),
35
+ };
36
+
37
+ const flattened = flatten(queryOptions);
38
+ for (const key in flattened) {
39
+ url.searchParams.set(key, String(flattened[key]));
40
+ }
41
+
42
+ if (userAttributes) {
43
+ url.searchParams.set('userAttributes', JSON.stringify(userAttributes));
44
+ }
45
+ if (query) {
46
+ const flattened = flatten({ query });
47
+ for (const key in flattened) {
48
+ url.searchParams.set(key, JSON.stringify((flattened as any)[key]));
49
+ }
50
+ }
51
+
52
+ return url;
53
+ };
54
+
55
+ interface ContentResponse {
56
+ results: BuilderContent[];
57
+ }
58
+
59
+ export async function getAllContent(
60
+ options: GetContentOptions
61
+ ): Promise<ContentResponse> {
62
+ const url = generateContentUrl(options);
63
+
64
+ const fetch = await getFetch();
65
+ const content: ContentResponse = await fetch(url.href).then((res) =>
66
+ res.json()
67
+ );
68
+
69
+ const canTrack = options.canTrack !== false;
70
+ if (canTrack) {
71
+ for (const item of content.results) {
72
+ await handleABTesting({ item, canTrack });
73
+ }
74
+ }
75
+
76
+ return content;
77
+ }
@@ -0,0 +1,36 @@
1
+ export interface GetContentOptions {
2
+ /** The model to get content for */
3
+ model: string;
4
+
5
+ /** Your public API key */
6
+ apiKey: string;
7
+
8
+ /** Number of items to fetch. Default is 1 */
9
+ limit?: number;
10
+
11
+ /** User attributes to target on, such as { urlPath: '/foo', device: 'mobile', ...etc } */
12
+ userAttributes?: (Record<string, string> & { urlPath?: string }) | null;
13
+
14
+ /** Custom query */
15
+ query?: Record<string, any>;
16
+
17
+ /**
18
+ * Any other API options.
19
+ * Accepts both a key/value object or a `URLSearchParams` instance
20
+ * */
21
+ options?: Record<string, any> | URLSearchParams;
22
+
23
+ /**
24
+ * If set to `true`, it will lazy load symbols/references.
25
+ * If set to `false`, it will render the entire content tree eagerly.
26
+ */
27
+ noTraverse?: boolean;
28
+
29
+ /**
30
+ * If set to `false`, it will not use cookies to target content. Therefore, A/B Testing will be disabled and
31
+ * only the default variation will be returned to every user.
32
+ *
33
+ * Defaults to `true`.
34
+ */
35
+ canTrack?: boolean;
36
+ }