@builder.io/sdk-svelte 5.2.16 → 5.3.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 (118) hide show
  1. package/README.md +4 -0
  2. package/lib/browser/components/block/block.helpers.d.ts +1 -3
  3. package/lib/browser/components/block/block.helpers.js +31 -4
  4. package/lib/browser/components/block/block.svelte +14 -1
  5. package/lib/browser/components/content/components/styles.helpers.d.ts +1 -1
  6. package/lib/browser/components/content/components/styles.helpers.js +1 -1
  7. package/lib/browser/components/content/content.helpers.d.ts +5 -1
  8. package/lib/browser/components/content/content.helpers.js +39 -2
  9. package/lib/browser/components/content/content.svelte +4 -17
  10. package/lib/browser/constants/builder-registered-components.d.ts +1 -0
  11. package/lib/browser/constants/builder-registered-components.js +40 -33
  12. package/lib/browser/constants/device-sizes.js +14 -2
  13. package/lib/browser/constants/sdk-version.d.ts +1 -1
  14. package/lib/browser/constants/sdk-version.js +1 -1
  15. package/lib/browser/functions/evaluate/browser-runtime/browser.js +13 -1
  16. package/lib/browser/functions/evaluate/evaluate.js +21 -5
  17. package/lib/browser/functions/evaluate/node-runtime/node-runtime.js +114 -23
  18. package/lib/browser/functions/evaluate/safe-state-expression.d.ts +33 -0
  19. package/lib/browser/functions/evaluate/safe-state-expression.js +236 -0
  20. package/lib/browser/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
  21. package/lib/browser/functions/extract-localized-values.d.ts +4 -0
  22. package/lib/browser/functions/extract-localized-values.js +65 -26
  23. package/lib/browser/functions/get-block-properties.js +2 -1
  24. package/lib/browser/functions/get-processed-block.d.ts +0 -1
  25. package/lib/browser/functions/get-processed-block.js +45 -57
  26. package/lib/browser/functions/is-editing-or-previewing.d.ts +1 -0
  27. package/lib/browser/functions/is-editing-or-previewing.js +3 -0
  28. package/lib/browser/functions/set.d.ts +29 -1
  29. package/lib/browser/functions/set.js +85 -4
  30. package/lib/browser/helpers/bounded-cache.d.ts +4 -0
  31. package/lib/browser/helpers/bounded-cache.js +12 -0
  32. package/lib/browser/helpers/copy-plain-data.d.ts +6 -0
  33. package/lib/browser/helpers/copy-plain-data.js +40 -0
  34. package/lib/edge/components/block/block.helpers.d.ts +1 -3
  35. package/lib/edge/components/block/block.helpers.js +31 -4
  36. package/lib/edge/components/block/block.svelte +14 -1
  37. package/lib/edge/components/content/components/styles.helpers.d.ts +1 -1
  38. package/lib/edge/components/content/components/styles.helpers.js +1 -1
  39. package/lib/edge/components/content/content.helpers.d.ts +5 -1
  40. package/lib/edge/components/content/content.helpers.js +39 -2
  41. package/lib/edge/components/content/content.svelte +4 -17
  42. package/lib/edge/constants/builder-registered-components.d.ts +1 -0
  43. package/lib/edge/constants/builder-registered-components.js +40 -33
  44. package/lib/edge/constants/device-sizes.js +14 -2
  45. package/lib/edge/constants/sdk-version.d.ts +1 -1
  46. package/lib/edge/constants/sdk-version.js +1 -1
  47. package/lib/edge/functions/evaluate/browser-runtime/browser.js +13 -1
  48. package/lib/edge/functions/evaluate/evaluate.js +21 -5
  49. package/lib/edge/functions/evaluate/node-runtime/node-runtime.js +114 -23
  50. package/lib/edge/functions/evaluate/safe-state-expression.d.ts +33 -0
  51. package/lib/edge/functions/evaluate/safe-state-expression.js +236 -0
  52. package/lib/edge/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
  53. package/lib/edge/functions/extract-localized-values.d.ts +4 -0
  54. package/lib/edge/functions/extract-localized-values.js +65 -26
  55. package/lib/edge/functions/get-block-properties.js +2 -1
  56. package/lib/edge/functions/get-processed-block.d.ts +0 -1
  57. package/lib/edge/functions/get-processed-block.js +45 -57
  58. package/lib/edge/functions/is-editing-or-previewing.d.ts +1 -0
  59. package/lib/edge/functions/is-editing-or-previewing.js +3 -0
  60. package/lib/edge/functions/set.d.ts +29 -1
  61. package/lib/edge/functions/set.js +85 -4
  62. package/lib/edge/helpers/bounded-cache.d.ts +4 -0
  63. package/lib/edge/helpers/bounded-cache.js +12 -0
  64. package/lib/edge/helpers/copy-plain-data.d.ts +6 -0
  65. package/lib/edge/helpers/copy-plain-data.js +40 -0
  66. package/lib/node/components/block/block.helpers.d.ts +1 -3
  67. package/lib/node/components/block/block.helpers.js +31 -4
  68. package/lib/node/components/block/block.svelte +14 -1
  69. package/lib/node/components/content/components/styles.helpers.d.ts +1 -1
  70. package/lib/node/components/content/components/styles.helpers.js +1 -1
  71. package/lib/node/components/content/content.helpers.d.ts +5 -1
  72. package/lib/node/components/content/content.helpers.js +39 -2
  73. package/lib/node/components/content/content.svelte +4 -17
  74. package/lib/node/constants/builder-registered-components.d.ts +1 -0
  75. package/lib/node/constants/builder-registered-components.js +40 -33
  76. package/lib/node/constants/device-sizes.js +14 -2
  77. package/lib/node/constants/sdk-version.d.ts +1 -1
  78. package/lib/node/constants/sdk-version.js +1 -1
  79. package/lib/node/functions/evaluate/browser-runtime/browser.js +13 -1
  80. package/lib/node/functions/evaluate/evaluate.js +21 -5
  81. package/lib/node/functions/evaluate/node-runtime/node-runtime.js +114 -23
  82. package/lib/node/functions/evaluate/safe-state-expression.d.ts +33 -0
  83. package/lib/node/functions/evaluate/safe-state-expression.js +236 -0
  84. package/lib/node/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
  85. package/lib/node/functions/extract-localized-values.d.ts +4 -0
  86. package/lib/node/functions/extract-localized-values.js +65 -26
  87. package/lib/node/functions/get-block-properties.js +2 -1
  88. package/lib/node/functions/get-processed-block.d.ts +0 -1
  89. package/lib/node/functions/get-processed-block.js +45 -57
  90. package/lib/node/functions/is-editing-or-previewing.d.ts +1 -0
  91. package/lib/node/functions/is-editing-or-previewing.js +3 -0
  92. package/lib/node/functions/set.d.ts +29 -1
  93. package/lib/node/functions/set.js +85 -4
  94. package/lib/node/helpers/bounded-cache.d.ts +4 -0
  95. package/lib/node/helpers/bounded-cache.js +12 -0
  96. package/lib/node/helpers/copy-plain-data.d.ts +6 -0
  97. package/lib/node/helpers/copy-plain-data.js +40 -0
  98. package/package.json +1 -1
  99. package/src/components/block/block.helpers.ts +38 -5
  100. package/src/components/block/block.svelte +14 -1
  101. package/src/components/content/components/styles.helpers.ts +1 -1
  102. package/src/components/content/content.helpers.ts +45 -4
  103. package/src/components/content/content.svelte +7 -24
  104. package/src/constants/builder-registered-components.ts +40 -34
  105. package/src/constants/device-sizes.ts +14 -3
  106. package/src/constants/sdk-version.ts +1 -1
  107. package/src/functions/evaluate/browser-runtime/browser.ts +14 -1
  108. package/src/functions/evaluate/evaluate.ts +22 -5
  109. package/src/functions/evaluate/node-runtime/node-runtime.ts +116 -25
  110. package/src/functions/evaluate/safe-state-expression.ts +224 -0
  111. package/src/functions/evaluate/should-force-browser-runtime-in-node.ts +3 -1
  112. package/src/functions/extract-localized-values.ts +61 -27
  113. package/src/functions/get-block-properties.ts +5 -1
  114. package/src/functions/get-processed-block.ts +46 -57
  115. package/src/functions/is-editing-or-previewing.ts +3 -0
  116. package/src/functions/set.ts +89 -4
  117. package/src/helpers/bounded-cache.ts +12 -0
  118. package/src/helpers/copy-plain-data.ts +36 -0
@@ -0,0 +1,12 @@
1
+ export const createBoundedCache = (max) => {
2
+ const map = new Map();
3
+ return {
4
+ get: (key) => map.get(key),
5
+ set: (key, value) => {
6
+ if (!map.has(key) && map.size >= max) {
7
+ map.delete(map.keys().next().value);
8
+ }
9
+ map.set(key, value);
10
+ }
11
+ };
12
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Deep-copies plain objects and arrays (anything else is kept by reference), so
3
+ * that state writes made while rendering never reach the content object, which
4
+ * apps often cache and share between requests.
5
+ */
6
+ export declare const copyPlainData: <T>(value: T, seen?: WeakMap<object, any>) => T;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Deep-copies plain objects and arrays (anything else is kept by reference), so
3
+ * that state writes made while rendering never reach the content object, which
4
+ * apps often cache and share between requests.
5
+ */
6
+ export const copyPlainData = (value, seen = new WeakMap()) => {
7
+ if (value === null || typeof value !== 'object')
8
+ return value;
9
+ if (seen.has(value))
10
+ return seen.get(value);
11
+ if (Array.isArray(value)) {
12
+ const copy = [];
13
+ seen.set(value, copy);
14
+ for (let i = 0; i < value.length; i++) {
15
+ copy[i] = copyPlainData(value[i], seen);
16
+ }
17
+ return copy;
18
+ }
19
+ const proto = Object.getPrototypeOf(value);
20
+ if (proto !== Object.prototype && proto !== null)
21
+ return value;
22
+ const copy = {};
23
+ seen.set(value, copy);
24
+ for (const key of Object.keys(value)) {
25
+ const copied = copyPlainData(value[key], seen);
26
+ if (key === '__proto__') {
27
+ // Assigning would call the prototype setter; JSON can carry `__proto__` as an own key.
28
+ Object.defineProperty(copy, key, {
29
+ value: copied,
30
+ enumerable: true,
31
+ writable: true,
32
+ configurable: true
33
+ });
34
+ }
35
+ else {
36
+ copy[key] = copied;
37
+ }
38
+ }
39
+ return copy;
40
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-svelte",
3
3
  "description": "Builder.io SDK for Svelte",
4
- "version": "5.2.16",
4
+ "version": "5.3.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "lib",
@@ -3,6 +3,7 @@ import type { BuilderContextInterface, RegisteredComponent, RegisteredComponents
3
3
  import { evaluate } from '../../functions/evaluate/index';
4
4
  import { extractTextStyles } from '../../functions/extract-text-styles';
5
5
  import { getStyle } from '../../functions/get-style';
6
+ import { isEditingOrPreviewing } from '../../functions/is-editing-or-previewing';
6
7
  import type { BuilderBlock } from '../../types/builder-block';
7
8
  import type { RepeatData } from './types';
8
9
  const checkIsComponentRestricted = (component: RegisteredComponent | null | undefined, model: string) => {
@@ -34,6 +35,28 @@ export const getComponent = ({
34
35
  return ref;
35
36
  }
36
37
  };
38
+
39
+ // Reused per block so repeated items keep a stable block identity across renders.
40
+ const BLOCKS_WITHOUT_REPEAT = new WeakMap<BuilderBlock, BuilderBlock>();
41
+ const getBlockWithoutRepeat = (block: BuilderBlock) => {
42
+ if (isEditingOrPreviewing()) {
43
+ const {
44
+ repeat: _repeat,
45
+ ...rest
46
+ } = block;
47
+ return rest;
48
+ }
49
+ let blockWithoutRepeat = BLOCKS_WITHOUT_REPEAT.get(block);
50
+ if (!blockWithoutRepeat) {
51
+ const {
52
+ repeat: _repeat,
53
+ ...rest
54
+ } = block;
55
+ blockWithoutRepeat = rest;
56
+ BLOCKS_WITHOUT_REPEAT.set(block, blockWithoutRepeat);
57
+ }
58
+ return blockWithoutRepeat;
59
+ };
37
60
  export const getRepeatItemData = ({
38
61
  block,
39
62
  context
@@ -46,8 +69,7 @@ export const getRepeatItemData = ({
46
69
  * which will not work if there is a repeat.
47
70
  */
48
71
  const {
49
- repeat,
50
- ...blockWithoutRepeat
72
+ repeat
51
73
  } = block;
52
74
  if (!repeat?.collection) {
53
75
  return undefined;
@@ -64,6 +86,7 @@ export const getRepeatItemData = ({
64
86
  }
65
87
  const collectionName = repeat.collection.split('.').pop();
66
88
  const itemNameToUse = repeat.itemName || (collectionName ? collectionName + 'Item' : 'item');
89
+ const blockWithoutRepeat = getBlockWithoutRepeat(block);
67
90
  const repeatArray = itemsArray.map<RepeatData>((item, index) => ({
68
91
  context: {
69
92
  ...context,
@@ -101,11 +124,21 @@ export const provideLinkComponent = (block: RegisteredComponent | null | undefin
101
124
  };
102
125
  return {};
103
126
  };
127
+ const FILTERED_REGISTERED_COMPONENTS = new WeakMap<RegisteredComponents, Map<string, RegisteredComponents>>();
104
128
  export const provideRegisteredComponents = (block: RegisteredComponent | null | undefined, registeredComponents: RegisteredComponents, model: string) => {
105
129
  if (block?.shouldReceiveBuilderProps?.builderComponents) {
106
- const filteredRegisteredComponents = Object.fromEntries(Object.entries(registeredComponents).filter(([_, component]) => {
107
- return !checkIsComponentRestricted(component, model);
108
- }));
130
+ let byModel = FILTERED_REGISTERED_COMPONENTS.get(registeredComponents);
131
+ if (!byModel) {
132
+ byModel = new Map();
133
+ FILTERED_REGISTERED_COMPONENTS.set(registeredComponents, byModel);
134
+ }
135
+ let filteredRegisteredComponents = byModel.get(model);
136
+ if (!filteredRegisteredComponents) {
137
+ filteredRegisteredComponents = Object.fromEntries(Object.entries(registeredComponents).filter(([_, component]) => {
138
+ return !checkIsComponentRestricted(component, model);
139
+ }));
140
+ byModel.set(model, filteredRegisteredComponents);
141
+ }
109
142
  return {
110
143
  builderComponents: filteredRegisteredComponents
111
144
  };
@@ -103,7 +103,15 @@
103
103
  : [];
104
104
  };
105
105
  $: componentRefProps = () => {
106
- return {
106
+ const deps: unknown[] = [
107
+ processedBlock(),
108
+ $context,
109
+ $context.rootState,
110
+ $context.localState,
111
+ registeredComponents,
112
+ linkComponent,
113
+ ];
114
+ const componentRefProps: ComponentProps = {
107
115
  blockChildren: processedBlock().children ?? [],
108
116
  componentRef: blockComponent?.()?.component,
109
117
  componentOptions: {
@@ -124,12 +132,17 @@
124
132
  includeBlockProps: blockComponent?.()?.noWrap === true,
125
133
  isInteractive: !(blockComponent?.()?.isRSC && TARGET === "rsc"),
126
134
  };
135
+ return componentRefProps;
127
136
  };
128
137
 
129
138
  let _processedBlock = {
130
139
  value: null as BuilderBlock | null,
131
140
  update: false,
132
141
  };
142
+ let _componentRefProps = {
143
+ value: null as ComponentProps | null,
144
+ deps: [] as unknown[],
145
+ };
133
146
 
134
147
  onMount(() => {
135
148
  const blockId = processedBlock().id;
@@ -76,7 +76,7 @@ export const getCss = ({
76
76
  return cssCode?.replace(/&/g, `div[builder-content-id="${contentId}"]`) || '';
77
77
  };
78
78
  const DEFAULT_STYLES = `
79
- .builder-button {
79
+ :where(.builder-button) {
80
80
  all: unset;
81
81
  }
82
82
 
@@ -1,19 +1,60 @@
1
- import type { BuilderRenderState } from '../../context/types';
1
+ import type { BuilderRenderState, RegisteredComponent, RegisteredComponents } from '../../context/types';
2
+ import { serializeIncludingFunctions } from '../../functions/register-component';
3
+ import { copyPlainData } from '../../helpers/copy-plain-data';
2
4
  import type { BuilderContent } from '../../types/builder-content';
3
- import type { Nullable } from '../../types/typescript';
5
+ import type { ComponentInfo } from '../../types/components';
6
+ import type { Dictionary, Nullable } from '../../types/typescript';
4
7
  import type { ContentProps } from './content.types';
8
+
9
+ // Registrations are usually module constants, so their derived forms are built once per object.
10
+ const SERIALIZED_COMPONENT_INFOS = new WeakMap<object, ComponentInfo>();
11
+ const REGISTERED_COMPONENT_ENTRIES = new WeakMap<object, RegisteredComponent>();
12
+ const getSerializedComponentInfo = (registration: RegisteredComponent): ComponentInfo => {
13
+ const cached = SERIALIZED_COMPONENT_INFOS.get(registration);
14
+ if (cached) return cached;
15
+ const {
16
+ component: _,
17
+ ...rest
18
+ } = registration;
19
+ const info: ComponentInfo = serializeIncludingFunctions((rest as ComponentInfo));
20
+ SERIALIZED_COMPONENT_INFOS.set(registration, info);
21
+ return info;
22
+ };
23
+ export const getRegisteredComponents = (registrations: RegisteredComponent[], wrapComponent: (component: any) => any): RegisteredComponents => {
24
+ const registeredComponents: RegisteredComponents = {};
25
+ for (const registration of registrations) {
26
+ let entry = REGISTERED_COMPONENT_ENTRIES.get(registration);
27
+ if (!entry) {
28
+ entry = ({
29
+ component: wrapComponent(registration.component),
30
+ ...getSerializedComponentInfo(registration)
31
+ } as RegisteredComponent);
32
+ REGISTERED_COMPONENT_ENTRIES.set(registration, entry);
33
+ }
34
+ registeredComponents[registration.name] = entry;
35
+ }
36
+ return registeredComponents;
37
+ };
38
+ export const getComponentInfos = (registrations: RegisteredComponent[]): Dictionary<ComponentInfo> => {
39
+ const componentInfos: Dictionary<ComponentInfo> = {};
40
+ for (const registration of registrations) {
41
+ componentInfos[registration.name] = getSerializedComponentInfo(registration);
42
+ }
43
+ return componentInfos;
44
+ };
5
45
  export const getRootStateInitialValue = ({
6
46
  content,
7
47
  data,
8
48
  locale
9
49
  }: Pick<ContentProps, 'content' | 'data' | 'locale'>) => {
10
50
  const defaultValues: BuilderRenderState = {};
11
- const initialState = content?.data?.state || {};
51
+ const seen = new WeakMap<object, any>();
52
+ const initialState = copyPlainData(content?.data?.state || {}, seen);
12
53
 
13
54
  // set default values for content state inputs
14
55
  content?.data?.inputs?.forEach(input => {
15
56
  if (input.name && input.defaultValue !== undefined) {
16
- defaultValues[input.name] = input.defaultValue;
57
+ defaultValues[input.name] = copyPlainData(input.defaultValue, seen);
17
58
  }
18
59
  });
19
60
  return {
@@ -8,13 +8,9 @@
8
8
  import type {
9
9
  BuilderContextInterface,
10
10
  BuilderRenderState,
11
- RegisteredComponents,
12
11
  } from "../../context/types";
13
12
  import { evaluate } from "../../functions/evaluate/evaluate";
14
- import { serializeIncludingFunctions } from "../../functions/register-component";
15
13
  import { logger } from "../../helpers/logger";
16
- import type { ComponentInfo } from "../../types/components";
17
- import type { Dictionary } from "../../types/typescript";
18
14
  import Blocks from "../blocks/blocks.svelte";
19
15
  import { getUpdateVariantVisibilityScript } from "../content-variants/helpers";
20
16
  import DynamicDiv from "../dynamic-div.svelte";
@@ -22,7 +18,9 @@
22
18
  import EnableEditor from "./components/enable-editor.svelte";
23
19
  import ContentStyles from "./components/styles.svelte";
24
20
  import {
21
+ getComponentInfos,
25
22
  getContentInitialValue,
23
+ getRegisteredComponents,
26
24
  getRootStateInitialValue,
27
25
  } from "./content.helpers";
28
26
  import type { ContentProps } from "./content.types";
@@ -62,18 +60,9 @@
62
60
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
63
61
  contentId: content?.id!,
64
62
  });
65
- let registeredComponents = [
66
- ...getDefaultRegisteredComponents(),
67
- ...(customComponents || []),
68
- ].reduce<RegisteredComponents>(
69
- (acc, { component, ...info }) => ({
70
- ...acc,
71
- [info.name]: {
72
- component: component,
73
- ...serializeIncludingFunctions(info),
74
- },
75
- }),
76
- {}
63
+ let registeredComponents = getRegisteredComponents(
64
+ [...getDefaultRegisteredComponents(), ...(customComponents || [])],
65
+ (component) => component
77
66
  );
78
67
  let builderContextSignal = writable({
79
68
  content: getContentInitialValue({
@@ -91,16 +80,10 @@
91
80
  canTrack: canTrack,
92
81
  apiKey: apiKey,
93
82
  apiVersion: apiVersion,
94
- componentInfos: [
83
+ componentInfos: getComponentInfos([
95
84
  ...getDefaultRegisteredComponents(),
96
85
  ...(customComponents || []),
97
- ].reduce<Dictionary<ComponentInfo>>(
98
- (acc, { component: _, ...info }) => ({
99
- ...acc,
100
- [info.name]: serializeIncludingFunctions(info),
101
- }),
102
- {}
103
- ),
86
+ ]),
104
87
  inheritedStyles: {},
105
88
  BlocksWrapper: blocksWrapper || "div",
106
89
  BlocksWrapperProps: blocksWrapperProps || {},
@@ -24,42 +24,48 @@ import { default as Text } from '../blocks/text/index';
24
24
  import type { RegisteredComponent } from '../context/types';
25
25
  import { getExtraComponents } from './extra-components';
26
26
  import { TARGET } from './target';
27
+ let DEFAULT_REGISTERED_COMPONENTS: RegisteredComponent[] | undefined;
27
28
 
28
29
  /**
29
30
  * Returns a list of all registered components.
30
31
  * NOTE: This needs to be a function to work around ESM circular dependencies.
32
+ * The list is built once, on first use, so its entries keep a stable identity.
31
33
  */
32
- export const getDefaultRegisteredComponents: () => RegisteredComponent[] = () => [{
33
- component: Button,
34
- ...buttonComponentInfo
35
- }, {
36
- component: Columns,
37
- ...columnsComponentInfo
38
- }, {
39
- component: Fragment,
40
- ...fragmentComponentInfo
41
- }, {
42
- component: Image,
43
- ...imageComponentInfo
44
- }, {
45
- component: Section,
46
- ...sectionComponentInfo
47
- }, {
48
- component: Slot,
49
- ...slotComponentInfo
50
- }, {
51
- component: Symbol,
52
- ...symbolComponentInfo
53
- }, {
54
- component: Text,
55
- ...textComponentInfo
56
- }, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
57
- component: PersonalizationContainer,
58
- ...personalizationContainerComponentInfo
59
- }] : []), ...(TARGET === 'rsc' ? [] : [{
60
- component: Tabs,
61
- ...tabsComponentInfo
62
- }, {
63
- component: Accordion,
64
- ...accordionComponentInfo
65
- }]), ...getExtraComponents()]
34
+ export const getDefaultRegisteredComponents: () => RegisteredComponent[] = () => {
35
+ if (DEFAULT_REGISTERED_COMPONENTS) return DEFAULT_REGISTERED_COMPONENTS;
36
+ DEFAULT_REGISTERED_COMPONENTS = [{
37
+ component: Button,
38
+ ...buttonComponentInfo
39
+ }, {
40
+ component: Columns,
41
+ ...columnsComponentInfo
42
+ }, {
43
+ component: Fragment,
44
+ ...fragmentComponentInfo
45
+ }, {
46
+ component: Image,
47
+ ...imageComponentInfo
48
+ }, {
49
+ component: Section,
50
+ ...sectionComponentInfo
51
+ }, {
52
+ component: Slot,
53
+ ...slotComponentInfo
54
+ }, {
55
+ component: Symbol,
56
+ ...symbolComponentInfo
57
+ }, {
58
+ component: Text,
59
+ ...textComponentInfo
60
+ }, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
61
+ component: PersonalizationContainer,
62
+ ...personalizationContainerComponentInfo
63
+ }] : []), ...(TARGET === 'rsc' ? [] : [{
64
+ component: Tabs,
65
+ ...tabsComponentInfo
66
+ }, {
67
+ component: Accordion,
68
+ ...accordionComponentInfo
69
+ }]), ...getExtraComponents()];
70
+ return DEFAULT_REGISTERED_COMPONENTS;
71
+ }
@@ -1,4 +1,3 @@
1
- import { fastClone } from '../functions/fast-clone';
2
1
  export type SizeName = 'large' | 'medium' | 'small' | 'xsmall';
3
2
  interface Size {
4
3
  min: number;
@@ -34,8 +33,20 @@ interface Breakpoints {
34
33
  medium?: number;
35
34
  }
36
35
  export const getSizesForBreakpoints = (breakpoints: Breakpoints) => {
37
- const newSizes = fastClone(SIZES); // Note: this helps to get a deep clone of fields like small, medium etc
38
-
36
+ const newSizes: Record<SizeName, Size> = {
37
+ xsmall: {
38
+ ...SIZES.xsmall
39
+ },
40
+ small: {
41
+ ...SIZES.small
42
+ },
43
+ medium: {
44
+ ...SIZES.medium
45
+ },
46
+ large: {
47
+ ...SIZES.large
48
+ }
49
+ };
39
50
  if (!breakpoints) {
40
51
  return newSizes;
41
52
  }
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.16"
1
+ export const SDK_VERSION = "5.3.0"
@@ -1,5 +1,18 @@
1
+ import { createBoundedCache } from '../../../helpers/bounded-cache';
1
2
  import type { ExecutorArgs } from '../helpers';
2
3
  import { flattenState, getFunctionArguments } from '../helpers';
4
+ const MAX_CACHED_FUNCTIONS = 5000;
5
+ const FUNCTION_CACHE = createBoundedCache<(...args: any[]) => any>(MAX_CACHED_FUNCTIONS);
6
+
7
+ // `getFunctionArguments` always returns the same argument names, so `code` alone is the key.
8
+ const getCompiledFunction = (argNames: string[], code: string) => {
9
+ let fn = FUNCTION_CACHE.get(code);
10
+ if (!fn) {
11
+ fn = (new Function(...argNames, code) as (...args: any[]) => any);
12
+ FUNCTION_CACHE.set(code, fn);
13
+ }
14
+ return fn;
15
+ };
3
16
  export const runInBrowser = ({
4
17
  code,
5
18
  builder,
@@ -19,5 +32,5 @@ export const runInBrowser = ({
19
32
  rootSetState
20
33
  })
21
34
  });
22
- return new Function(...functionArgs.map(([name]) => name), code)(...functionArgs.map(([, value]) => value));
35
+ return getCompiledFunction(functionArgs.map(([name]) => name), code)(...functionArgs.map(([, value]) => value));
23
36
  }
@@ -1,3 +1,4 @@
1
+ import { createBoundedCache } from '../../helpers/bounded-cache';
1
2
  import { logger } from '../../helpers/logger';
2
3
  import { get } from '../get';
3
4
  import { chooseBrowserOrServerEval } from './choose-eval';
@@ -20,6 +21,25 @@ const VIRTUAL_INDEX_REGEX = /(\s)*var(\s)+_virtual_index(\s)*=(\s)*state(?<getPa
20
21
  export const getSimpleExpressionGetPath = (code: string) => {
21
22
  return STATE_GETTER_REGEX.exec(code.trim())?.groups?.getPath?.slice(1) || VIRTUAL_INDEX_REGEX.exec(code.trim())?.groups?.getPath?.slice(1);
22
23
  };
24
+ const MAX_CACHED_CODE_STRINGS = 5000;
25
+ const SIMPLE_GET_PATH_CACHE = createBoundedCache<string | null>(MAX_CACHED_CODE_STRINGS);
26
+ const getCachedSimpleExpressionGetPath = (code: string) => {
27
+ let getPath = SIMPLE_GET_PATH_CACHE.get(code);
28
+ if (getPath === undefined) {
29
+ getPath = getSimpleExpressionGetPath(code) || null;
30
+ SIMPLE_GET_PATH_CACHE.set(code, getPath);
31
+ }
32
+ return getPath;
33
+ };
34
+
35
+ /**
36
+ * Same result as `get({ ...rootState, ...localState }, getPath)`, without copying every state key.
37
+ */
38
+ const getSimpleStateValue = (rootState: EvaluatorArgs['rootState'], localState: EvaluatorArgs['localState'], getPath: string) => {
39
+ const firstKey = getPath.split('.')[0];
40
+ const source = localState && Object.prototype.hasOwnProperty.call(localState, firstKey) ? localState : rootState;
41
+ return get(source, getPath);
42
+ };
23
43
  export function evaluate({
24
44
  code,
25
45
  context,
@@ -40,12 +60,9 @@ export function evaluate({
40
60
  * We try not to take many risks with this optimizations, so we only do it for
41
61
  * `state.{path}` expressions.
42
62
  */
43
- const getPath = getSimpleExpressionGetPath(code.trim());
63
+ const getPath = getCachedSimpleExpressionGetPath(code.trim());
44
64
  if (getPath) {
45
- return get({
46
- ...rootState,
47
- ...localState
48
- }, getPath);
65
+ return getSimpleStateValue(rootState, localState, getPath);
49
66
  }
50
67
  const args: ExecutorArgs = {
51
68
  code: parseCode(code, {