@builder.io/sdk-svelte 5.2.16 → 5.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.
- package/README.md +4 -0
- package/lib/browser/blocks/symbol/symbol.helpers.d.ts +1 -0
- package/lib/browser/blocks/symbol/symbol.helpers.js +6 -0
- package/lib/browser/blocks/symbol/symbol.svelte +5 -1
- package/lib/browser/components/block/block.helpers.d.ts +1 -3
- package/lib/browser/components/block/block.helpers.js +31 -4
- package/lib/browser/components/block/block.svelte +14 -1
- package/lib/browser/components/builder-scripts.svelte +39 -0
- package/lib/browser/components/builder-scripts.svelte.d.ts +22 -0
- package/lib/browser/components/content/components/styles.helpers.d.ts +1 -1
- package/lib/browser/components/content/components/styles.helpers.js +1 -1
- package/lib/browser/components/content/content.helpers.d.ts +5 -1
- package/lib/browser/components/content/content.helpers.js +39 -2
- package/lib/browser/components/content/content.svelte +4 -17
- package/lib/browser/components/content-variants/content-variants.svelte +15 -8
- package/lib/browser/components/content-variants/content-variants.svelte.d.ts +5 -0
- package/lib/browser/components/content-variants/helpers.d.ts +6 -0
- package/lib/browser/components/content-variants/helpers.js +5 -0
- package/lib/browser/constants/builder-registered-components.d.ts +1 -0
- package/lib/browser/constants/builder-registered-components.js +40 -33
- package/lib/browser/constants/device-sizes.js +14 -2
- package/lib/browser/constants/sdk-version.d.ts +1 -1
- package/lib/browser/constants/sdk-version.js +1 -1
- package/lib/browser/context/builder-scripts.context.d.ts +7 -0
- package/lib/browser/context/builder-scripts.context.js +5 -0
- package/lib/browser/functions/evaluate/browser-runtime/browser.js +13 -1
- package/lib/browser/functions/evaluate/evaluate.js +21 -5
- package/lib/browser/functions/evaluate/node-runtime/node-runtime.js +114 -23
- package/lib/browser/functions/evaluate/safe-state-expression.d.ts +33 -0
- package/lib/browser/functions/evaluate/safe-state-expression.js +236 -0
- package/lib/browser/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
- package/lib/browser/functions/extract-localized-values.d.ts +4 -0
- package/lib/browser/functions/extract-localized-values.js +65 -26
- package/lib/browser/functions/get-block-properties.js +2 -1
- package/lib/browser/functions/get-processed-block.d.ts +0 -1
- package/lib/browser/functions/get-processed-block.js +45 -57
- package/lib/browser/functions/is-editing-or-previewing.d.ts +1 -0
- package/lib/browser/functions/is-editing-or-previewing.js +3 -0
- package/lib/browser/functions/set.d.ts +29 -1
- package/lib/browser/functions/set.js +85 -4
- package/lib/browser/helpers/bounded-cache.d.ts +4 -0
- package/lib/browser/helpers/bounded-cache.js +12 -0
- package/lib/browser/helpers/copy-plain-data.d.ts +6 -0
- package/lib/browser/helpers/copy-plain-data.js +40 -0
- package/lib/browser/index-helpers/blocks-exports.d.ts +1 -0
- package/lib/browser/index-helpers/blocks-exports.js +1 -0
- package/lib/edge/blocks/symbol/symbol.helpers.d.ts +1 -0
- package/lib/edge/blocks/symbol/symbol.helpers.js +6 -0
- package/lib/edge/blocks/symbol/symbol.svelte +5 -1
- package/lib/edge/components/block/block.helpers.d.ts +1 -3
- package/lib/edge/components/block/block.helpers.js +31 -4
- package/lib/edge/components/block/block.svelte +14 -1
- package/lib/edge/components/builder-scripts.svelte +39 -0
- package/lib/edge/components/builder-scripts.svelte.d.ts +22 -0
- package/lib/edge/components/content/components/styles.helpers.d.ts +1 -1
- package/lib/edge/components/content/components/styles.helpers.js +1 -1
- package/lib/edge/components/content/content.helpers.d.ts +5 -1
- package/lib/edge/components/content/content.helpers.js +39 -2
- package/lib/edge/components/content/content.svelte +4 -17
- package/lib/edge/components/content-variants/content-variants.svelte +15 -8
- package/lib/edge/components/content-variants/content-variants.svelte.d.ts +5 -0
- package/lib/edge/components/content-variants/helpers.d.ts +6 -0
- package/lib/edge/components/content-variants/helpers.js +5 -0
- package/lib/edge/constants/builder-registered-components.d.ts +1 -0
- package/lib/edge/constants/builder-registered-components.js +40 -33
- package/lib/edge/constants/device-sizes.js +14 -2
- package/lib/edge/constants/sdk-version.d.ts +1 -1
- package/lib/edge/constants/sdk-version.js +1 -1
- package/lib/edge/context/builder-scripts.context.d.ts +7 -0
- package/lib/edge/context/builder-scripts.context.js +5 -0
- package/lib/edge/functions/evaluate/browser-runtime/browser.js +13 -1
- package/lib/edge/functions/evaluate/evaluate.js +21 -5
- package/lib/edge/functions/evaluate/node-runtime/node-runtime.js +114 -23
- package/lib/edge/functions/evaluate/safe-state-expression.d.ts +33 -0
- package/lib/edge/functions/evaluate/safe-state-expression.js +236 -0
- package/lib/edge/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
- package/lib/edge/functions/extract-localized-values.d.ts +4 -0
- package/lib/edge/functions/extract-localized-values.js +65 -26
- package/lib/edge/functions/get-block-properties.js +2 -1
- package/lib/edge/functions/get-processed-block.d.ts +0 -1
- package/lib/edge/functions/get-processed-block.js +45 -57
- package/lib/edge/functions/is-editing-or-previewing.d.ts +1 -0
- package/lib/edge/functions/is-editing-or-previewing.js +3 -0
- package/lib/edge/functions/set.d.ts +29 -1
- package/lib/edge/functions/set.js +85 -4
- package/lib/edge/helpers/bounded-cache.d.ts +4 -0
- package/lib/edge/helpers/bounded-cache.js +12 -0
- package/lib/edge/helpers/copy-plain-data.d.ts +6 -0
- package/lib/edge/helpers/copy-plain-data.js +40 -0
- package/lib/edge/index-helpers/blocks-exports.d.ts +1 -0
- package/lib/edge/index-helpers/blocks-exports.js +1 -0
- package/lib/node/blocks/symbol/symbol.helpers.d.ts +1 -0
- package/lib/node/blocks/symbol/symbol.helpers.js +6 -0
- package/lib/node/blocks/symbol/symbol.svelte +5 -1
- package/lib/node/components/block/block.helpers.d.ts +1 -3
- package/lib/node/components/block/block.helpers.js +31 -4
- package/lib/node/components/block/block.svelte +14 -1
- package/lib/node/components/builder-scripts.svelte +39 -0
- package/lib/node/components/builder-scripts.svelte.d.ts +22 -0
- package/lib/node/components/content/components/styles.helpers.d.ts +1 -1
- package/lib/node/components/content/components/styles.helpers.js +1 -1
- package/lib/node/components/content/content.helpers.d.ts +5 -1
- package/lib/node/components/content/content.helpers.js +39 -2
- package/lib/node/components/content/content.svelte +4 -17
- package/lib/node/components/content-variants/content-variants.svelte +15 -8
- package/lib/node/components/content-variants/content-variants.svelte.d.ts +5 -0
- package/lib/node/components/content-variants/helpers.d.ts +6 -0
- package/lib/node/components/content-variants/helpers.js +5 -0
- package/lib/node/constants/builder-registered-components.d.ts +1 -0
- package/lib/node/constants/builder-registered-components.js +40 -33
- package/lib/node/constants/device-sizes.js +14 -2
- package/lib/node/constants/sdk-version.d.ts +1 -1
- package/lib/node/constants/sdk-version.js +1 -1
- package/lib/node/context/builder-scripts.context.d.ts +7 -0
- package/lib/node/context/builder-scripts.context.js +5 -0
- package/lib/node/functions/evaluate/browser-runtime/browser.js +13 -1
- package/lib/node/functions/evaluate/evaluate.js +21 -5
- package/lib/node/functions/evaluate/node-runtime/node-runtime.js +114 -23
- package/lib/node/functions/evaluate/safe-state-expression.d.ts +33 -0
- package/lib/node/functions/evaluate/safe-state-expression.js +236 -0
- package/lib/node/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
- package/lib/node/functions/extract-localized-values.d.ts +4 -0
- package/lib/node/functions/extract-localized-values.js +65 -26
- package/lib/node/functions/get-block-properties.js +2 -1
- package/lib/node/functions/get-processed-block.d.ts +0 -1
- package/lib/node/functions/get-processed-block.js +45 -57
- package/lib/node/functions/is-editing-or-previewing.d.ts +1 -0
- package/lib/node/functions/is-editing-or-previewing.js +3 -0
- package/lib/node/functions/set.d.ts +29 -1
- package/lib/node/functions/set.js +85 -4
- package/lib/node/helpers/bounded-cache.d.ts +4 -0
- package/lib/node/helpers/bounded-cache.js +12 -0
- package/lib/node/helpers/copy-plain-data.d.ts +6 -0
- package/lib/node/helpers/copy-plain-data.js +40 -0
- package/lib/node/index-helpers/blocks-exports.d.ts +1 -0
- package/lib/node/index-helpers/blocks-exports.js +1 -0
- package/package.json +1 -1
- package/src/blocks/symbol/symbol.helpers.ts +7 -0
- package/src/blocks/symbol/symbol.svelte +8 -1
- package/src/components/block/block.helpers.ts +38 -5
- package/src/components/block/block.svelte +14 -1
- package/src/components/builder-scripts.svelte +57 -0
- package/src/components/content/components/styles.helpers.ts +1 -1
- package/src/components/content/content.helpers.ts +45 -4
- package/src/components/content/content.svelte +7 -24
- package/src/components/content-variants/content-variants.svelte +21 -8
- package/src/components/content-variants/helpers.ts +6 -0
- package/src/constants/builder-registered-components.ts +40 -34
- package/src/constants/device-sizes.ts +14 -3
- package/src/constants/sdk-version.ts +1 -1
- package/src/context/builder-scripts.context.ts +6 -0
- package/src/functions/evaluate/browser-runtime/browser.ts +14 -1
- package/src/functions/evaluate/evaluate.ts +22 -5
- package/src/functions/evaluate/node-runtime/node-runtime.ts +116 -25
- package/src/functions/evaluate/safe-state-expression.ts +224 -0
- package/src/functions/evaluate/should-force-browser-runtime-in-node.ts +3 -1
- package/src/functions/extract-localized-values.ts +61 -27
- package/src/functions/get-block-properties.ts +5 -1
- package/src/functions/get-processed-block.ts +46 -57
- package/src/functions/is-editing-or-previewing.ts +3 -0
- package/src/functions/set.ts +89 -4
- package/src/helpers/bounded-cache.ts +12 -0
- package/src/helpers/copy-plain-data.ts +36 -0
- package/src/index-helpers/blocks-exports.ts +1 -0
|
@@ -1,10 +1,47 @@
|
|
|
1
|
+
import { serializeIncludingFunctions } from '../../functions/register-component';
|
|
2
|
+
import { copyPlainData } from '../../helpers/copy-plain-data';
|
|
3
|
+
// Registrations are usually module constants, so their derived forms are built once per object.
|
|
4
|
+
const SERIALIZED_COMPONENT_INFOS = new WeakMap();
|
|
5
|
+
const REGISTERED_COMPONENT_ENTRIES = new WeakMap();
|
|
6
|
+
const getSerializedComponentInfo = (registration) => {
|
|
7
|
+
const cached = SERIALIZED_COMPONENT_INFOS.get(registration);
|
|
8
|
+
if (cached)
|
|
9
|
+
return cached;
|
|
10
|
+
const { component: _, ...rest } = registration;
|
|
11
|
+
const info = serializeIncludingFunctions(rest);
|
|
12
|
+
SERIALIZED_COMPONENT_INFOS.set(registration, info);
|
|
13
|
+
return info;
|
|
14
|
+
};
|
|
15
|
+
export const getRegisteredComponents = (registrations, wrapComponent) => {
|
|
16
|
+
const registeredComponents = {};
|
|
17
|
+
for (const registration of registrations) {
|
|
18
|
+
let entry = REGISTERED_COMPONENT_ENTRIES.get(registration);
|
|
19
|
+
if (!entry) {
|
|
20
|
+
entry = {
|
|
21
|
+
component: wrapComponent(registration.component),
|
|
22
|
+
...getSerializedComponentInfo(registration)
|
|
23
|
+
};
|
|
24
|
+
REGISTERED_COMPONENT_ENTRIES.set(registration, entry);
|
|
25
|
+
}
|
|
26
|
+
registeredComponents[registration.name] = entry;
|
|
27
|
+
}
|
|
28
|
+
return registeredComponents;
|
|
29
|
+
};
|
|
30
|
+
export const getComponentInfos = (registrations) => {
|
|
31
|
+
const componentInfos = {};
|
|
32
|
+
for (const registration of registrations) {
|
|
33
|
+
componentInfos[registration.name] = getSerializedComponentInfo(registration);
|
|
34
|
+
}
|
|
35
|
+
return componentInfos;
|
|
36
|
+
};
|
|
1
37
|
export const getRootStateInitialValue = ({ content, data, locale }) => {
|
|
2
38
|
const defaultValues = {};
|
|
3
|
-
const
|
|
39
|
+
const seen = new WeakMap();
|
|
40
|
+
const initialState = copyPlainData(content?.data?.state || {}, seen);
|
|
4
41
|
// set default values for content state inputs
|
|
5
42
|
content?.data?.inputs?.forEach(input => {
|
|
6
43
|
if (input.name && input.defaultValue !== undefined) {
|
|
7
|
-
defaultValues[input.name] = input.defaultValue;
|
|
44
|
+
defaultValues[input.name] = copyPlainData(input.defaultValue, seen);
|
|
8
45
|
}
|
|
9
46
|
});
|
|
10
47
|
return {
|
|
@@ -4,7 +4,6 @@ import { getDefaultRegisteredComponents } from "../../constants/builder-register
|
|
|
4
4
|
import { TARGET } from "../../constants/target";
|
|
5
5
|
import ComponentsContext from "../../context/components.context";
|
|
6
6
|
import { evaluate } from "../../functions/evaluate/evaluate";
|
|
7
|
-
import { serializeIncludingFunctions } from "../../functions/register-component";
|
|
8
7
|
import { logger } from "../../helpers/logger";
|
|
9
8
|
import Blocks from "../blocks/blocks.svelte";
|
|
10
9
|
import { getUpdateVariantVisibilityScript } from "../content-variants/helpers";
|
|
@@ -12,7 +11,7 @@ import "../dynamic-div.svelte";
|
|
|
12
11
|
import InlinedScript from "../inlined-script.svelte";
|
|
13
12
|
import EnableEditor from "./components/enable-editor.svelte";
|
|
14
13
|
import ContentStyles from "./components/styles.svelte";
|
|
15
|
-
import { getContentInitialValue, getRootStateInitialValue, } from "./content.helpers";
|
|
14
|
+
import { getComponentInfos, getContentInitialValue, getRegisteredComponents, getRootStateInitialValue, } from "./content.helpers";
|
|
16
15
|
import "./wrap-component-ref";
|
|
17
16
|
export let content;
|
|
18
17
|
export let data = undefined;
|
|
@@ -45,16 +44,7 @@ let scriptStr = getUpdateVariantVisibilityScript({
|
|
|
45
44
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
|
|
46
45
|
contentId: content?.id,
|
|
47
46
|
});
|
|
48
|
-
let registeredComponents = [
|
|
49
|
-
...getDefaultRegisteredComponents(),
|
|
50
|
-
...(customComponents || []),
|
|
51
|
-
].reduce((acc, { component, ...info }) => ({
|
|
52
|
-
...acc,
|
|
53
|
-
[info.name]: {
|
|
54
|
-
component: component,
|
|
55
|
-
...serializeIncludingFunctions(info),
|
|
56
|
-
},
|
|
57
|
-
}), {});
|
|
47
|
+
let registeredComponents = getRegisteredComponents([...getDefaultRegisteredComponents(), ...(customComponents || [])], (component) => component);
|
|
58
48
|
let builderContextSignal = writable({
|
|
59
49
|
content: getContentInitialValue({
|
|
60
50
|
content: content,
|
|
@@ -71,13 +61,10 @@ let builderContextSignal = writable({
|
|
|
71
61
|
canTrack: canTrack,
|
|
72
62
|
apiKey: apiKey,
|
|
73
63
|
apiVersion: apiVersion,
|
|
74
|
-
componentInfos: [
|
|
64
|
+
componentInfos: getComponentInfos([
|
|
75
65
|
...getDefaultRegisteredComponents(),
|
|
76
66
|
...(customComponents || []),
|
|
77
|
-
]
|
|
78
|
-
...acc,
|
|
79
|
-
[info.name]: serializeIncludingFunctions(info),
|
|
80
|
-
}), {}),
|
|
67
|
+
]),
|
|
81
68
|
inheritedStyles: {},
|
|
82
69
|
BlocksWrapper: blocksWrapper || "div",
|
|
83
70
|
BlocksWrapperProps: blocksWrapperProps || {},
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script context="module"></script>
|
|
2
2
|
|
|
3
|
-
<script>import { onMount } from "svelte";
|
|
3
|
+
<script>import { getContext, onMount } from "svelte";
|
|
4
4
|
import { SDKS_SUPPORTING_PERSONALIZATION, getInitPersonalizationVariantsFnsScriptString, hasPersonalizationContainer, } from "../../blocks/personalization-container/helpers";
|
|
5
5
|
import { TARGET } from "../../constants/target";
|
|
6
|
+
import BuilderScriptsContext from "../../context/builder-scripts.context";
|
|
6
7
|
import { handleABTestingSync } from "../../helpers/ab-tests";
|
|
7
8
|
import { getDefaultCanTrack } from "../../helpers/canTrack";
|
|
8
9
|
import ContentComponent from "../content/content.svelte";
|
|
@@ -11,6 +12,7 @@ import InlinedStyles from "../inlined-styles.svelte";
|
|
|
11
12
|
import { checkShouldRenderVariants, getInitVariantsFnsScriptString, getUpdateCookieAndStylesScript, getVariants, } from "./helpers";
|
|
12
13
|
export let canTrack = undefined;
|
|
13
14
|
export let content;
|
|
15
|
+
export let isContentInlinedInParent = undefined;
|
|
14
16
|
export let nonce = undefined;
|
|
15
17
|
export let apiHost = undefined;
|
|
16
18
|
export let isNestedRender = undefined;
|
|
@@ -29,6 +31,7 @@ export let blocksWrapperProps = undefined;
|
|
|
29
31
|
export let contentWrapper = undefined;
|
|
30
32
|
export let contentWrapperProps = undefined;
|
|
31
33
|
export let trustedHosts = undefined;
|
|
34
|
+
let scriptsContext = getContext(BuilderScriptsContext.key);
|
|
32
35
|
$: updateCookieAndStylesScriptStr = () => {
|
|
33
36
|
return getUpdateCookieAndStylesScript(getVariants(content).map((value) => ({
|
|
34
37
|
id: value.testVariationId,
|
|
@@ -55,7 +58,9 @@ let shouldRenderVariants = checkShouldRenderVariants({
|
|
|
55
58
|
canTrack: getDefaultCanTrack(canTrack),
|
|
56
59
|
content: content,
|
|
57
60
|
});
|
|
58
|
-
let shouldEmitPersonalizationFns = TARGET !== "reactNative" &&
|
|
61
|
+
let shouldEmitPersonalizationFns = TARGET !== "reactNative" &&
|
|
62
|
+
!isContentInlinedInParent &&
|
|
63
|
+
hasPersonalizationContainer(content);
|
|
59
64
|
onMount(() => {
|
|
60
65
|
/**
|
|
61
66
|
* For Solid/Svelte: we unmount the non-winning variants post-hydration.
|
|
@@ -64,7 +69,7 @@ onMount(() => {
|
|
|
64
69
|
});
|
|
65
70
|
</script>
|
|
66
71
|
|
|
67
|
-
{#if shouldEmitPersonalizationFns}
|
|
72
|
+
{#if shouldEmitPersonalizationFns && !!!scriptsContext?.scriptsEmitted}
|
|
68
73
|
{#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
|
|
69
74
|
<InlinedScript
|
|
70
75
|
id="builderio-init-personalization-variants-fns"
|
|
@@ -75,11 +80,13 @@ onMount(() => {
|
|
|
75
80
|
{/if}
|
|
76
81
|
|
|
77
82
|
{#if shouldRenderVariants}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
{#if !!!scriptsContext?.scriptsEmitted}
|
|
84
|
+
<InlinedScript
|
|
85
|
+
id="builderio-init-variants-fns"
|
|
86
|
+
scriptStr={getInitVariantsFnsScriptString()}
|
|
87
|
+
nonce={nonce || ""}
|
|
88
|
+
/>
|
|
89
|
+
{/if}
|
|
83
90
|
<InlinedStyles
|
|
84
91
|
id="builderio-variants"
|
|
85
92
|
styles={hideVariantsStyleString()}
|
|
@@ -4,12 +4,17 @@ type VariantsProviderProps = ContentVariantsPrps & {
|
|
|
4
4
|
* For internal use only. Do not provide this prop.
|
|
5
5
|
*/
|
|
6
6
|
isNestedRender?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* For internal use only. Do not provide this prop.
|
|
9
|
+
*/
|
|
10
|
+
isContentInlinedInParent?: boolean;
|
|
7
11
|
};
|
|
8
12
|
import type { ContentVariantsPrps } from "./content-variants.types";
|
|
9
13
|
declare const __propDef: {
|
|
10
14
|
props: {
|
|
11
15
|
canTrack?: VariantsProviderProps["canTrack"];
|
|
12
16
|
content: VariantsProviderProps["content"];
|
|
17
|
+
isContentInlinedInParent?: VariantsProviderProps["isContentInlinedInParent"];
|
|
13
18
|
nonce?: VariantsProviderProps["nonce"];
|
|
14
19
|
apiHost?: VariantsProviderProps["apiHost"];
|
|
15
20
|
isNestedRender?: VariantsProviderProps["isNestedRender"];
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Nullable } from '../../helpers/nullable';
|
|
2
2
|
import type { BuilderContent } from '../../types/builder-content';
|
|
3
|
+
import type { Target } from '../../types/targets';
|
|
4
|
+
/**
|
|
5
|
+
* SDKs where `BuilderScripts` can dedupe helper scripts. Elsewhere `Content` cannot read its context
|
|
6
|
+
* (RSC server components, Angular DI) or has no inline scripts (React Native).
|
|
7
|
+
*/
|
|
8
|
+
export declare const SDKS_SUPPORTING_BUILDER_SCRIPTS: Target[];
|
|
3
9
|
export declare const getVariants: (content: Nullable<BuilderContent>) => {
|
|
4
10
|
testVariationId: string | undefined;
|
|
5
11
|
id: string | undefined;
|
|
@@ -9,6 +9,11 @@ import { UPDATE_COOKIES_AND_STYLES_SCRIPT, UPDATE_VARIANT_VISIBILITY_SCRIPT } fr
|
|
|
9
9
|
*/
|
|
10
10
|
const UPDATE_COOKIES_AND_STYLES_SCRIPT_NAME = 'builderIoAbTest';
|
|
11
11
|
const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'builderIoRenderContent';
|
|
12
|
+
/**
|
|
13
|
+
* SDKs where `BuilderScripts` can dedupe helper scripts. Elsewhere `Content` cannot read its context
|
|
14
|
+
* (RSC server components, Angular DI) or has no inline scripts (React Native).
|
|
15
|
+
*/
|
|
16
|
+
export const SDKS_SUPPORTING_BUILDER_SCRIPTS = ['react', 'vue', 'svelte', 'solid', 'qwik'];
|
|
12
17
|
export const getVariants = (content) => Object.values(content?.variations || {}).map(variant => ({
|
|
13
18
|
...variant,
|
|
14
19
|
testVariationId: variant.id,
|
|
@@ -2,5 +2,6 @@ import type { RegisteredComponent } from '../context/types';
|
|
|
2
2
|
/**
|
|
3
3
|
* Returns a list of all registered components.
|
|
4
4
|
* NOTE: This needs to be a function to work around ESM circular dependencies.
|
|
5
|
+
* The list is built once, on first use, so its entries keep a stable identity.
|
|
5
6
|
*/
|
|
6
7
|
export declare const getDefaultRegisteredComponents: () => RegisteredComponent[];
|
|
@@ -23,41 +23,48 @@ import { componentInfo as textComponentInfo } from '../blocks/text/component-inf
|
|
|
23
23
|
import { default as Text } from '../blocks/text/index';
|
|
24
24
|
import { getExtraComponents } from './extra-components';
|
|
25
25
|
import { TARGET } from './target';
|
|
26
|
+
let DEFAULT_REGISTERED_COMPONENTS;
|
|
26
27
|
/**
|
|
27
28
|
* Returns a list of all registered components.
|
|
28
29
|
* NOTE: This needs to be a function to work around ESM circular dependencies.
|
|
30
|
+
* The list is built once, on first use, so its entries keep a stable identity.
|
|
29
31
|
*/
|
|
30
|
-
export const getDefaultRegisteredComponents = () =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}, {
|
|
37
|
-
component: Fragment,
|
|
38
|
-
...fragmentComponentInfo
|
|
39
|
-
}, {
|
|
40
|
-
component: Image,
|
|
41
|
-
...imageComponentInfo
|
|
42
|
-
}, {
|
|
43
|
-
component: Section,
|
|
44
|
-
...sectionComponentInfo
|
|
45
|
-
}, {
|
|
46
|
-
component: Slot,
|
|
47
|
-
...slotComponentInfo
|
|
48
|
-
}, {
|
|
49
|
-
component: Symbol,
|
|
50
|
-
...symbolComponentInfo
|
|
51
|
-
}, {
|
|
52
|
-
component: Text,
|
|
53
|
-
...textComponentInfo
|
|
54
|
-
}, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
|
|
55
|
-
component: PersonalizationContainer,
|
|
56
|
-
...personalizationContainerComponentInfo
|
|
57
|
-
}] : []), ...(TARGET === 'rsc' ? [] : [{
|
|
58
|
-
component: Tabs,
|
|
59
|
-
...tabsComponentInfo
|
|
32
|
+
export const getDefaultRegisteredComponents = () => {
|
|
33
|
+
if (DEFAULT_REGISTERED_COMPONENTS)
|
|
34
|
+
return DEFAULT_REGISTERED_COMPONENTS;
|
|
35
|
+
DEFAULT_REGISTERED_COMPONENTS = [{
|
|
36
|
+
component: Button,
|
|
37
|
+
...buttonComponentInfo
|
|
60
38
|
}, {
|
|
61
|
-
component:
|
|
62
|
-
...
|
|
63
|
-
}
|
|
39
|
+
component: Columns,
|
|
40
|
+
...columnsComponentInfo
|
|
41
|
+
}, {
|
|
42
|
+
component: Fragment,
|
|
43
|
+
...fragmentComponentInfo
|
|
44
|
+
}, {
|
|
45
|
+
component: Image,
|
|
46
|
+
...imageComponentInfo
|
|
47
|
+
}, {
|
|
48
|
+
component: Section,
|
|
49
|
+
...sectionComponentInfo
|
|
50
|
+
}, {
|
|
51
|
+
component: Slot,
|
|
52
|
+
...slotComponentInfo
|
|
53
|
+
}, {
|
|
54
|
+
component: Symbol,
|
|
55
|
+
...symbolComponentInfo
|
|
56
|
+
}, {
|
|
57
|
+
component: Text,
|
|
58
|
+
...textComponentInfo
|
|
59
|
+
}, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
|
|
60
|
+
component: PersonalizationContainer,
|
|
61
|
+
...personalizationContainerComponentInfo
|
|
62
|
+
}] : []), ...(TARGET === 'rsc' ? [] : [{
|
|
63
|
+
component: Tabs,
|
|
64
|
+
...tabsComponentInfo
|
|
65
|
+
}, {
|
|
66
|
+
component: Accordion,
|
|
67
|
+
...accordionComponentInfo
|
|
68
|
+
}]), ...getExtraComponents()];
|
|
69
|
+
return DEFAULT_REGISTERED_COMPONENTS;
|
|
70
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { fastClone } from '../functions/fast-clone';
|
|
2
1
|
const SIZES = {
|
|
3
2
|
xsmall: {
|
|
4
3
|
min: 0,
|
|
@@ -23,7 +22,20 @@ const SIZES = {
|
|
|
23
22
|
};
|
|
24
23
|
export const getMaxWidthQueryForSize = (size, sizeValues = SIZES) => `@media (max-width: ${sizeValues[size].max}px)`;
|
|
25
24
|
export const getSizesForBreakpoints = (breakpoints) => {
|
|
26
|
-
const newSizes =
|
|
25
|
+
const newSizes = {
|
|
26
|
+
xsmall: {
|
|
27
|
+
...SIZES.xsmall
|
|
28
|
+
},
|
|
29
|
+
small: {
|
|
30
|
+
...SIZES.small
|
|
31
|
+
},
|
|
32
|
+
medium: {
|
|
33
|
+
...SIZES.medium
|
|
34
|
+
},
|
|
35
|
+
large: {
|
|
36
|
+
...SIZES.large
|
|
37
|
+
}
|
|
38
|
+
};
|
|
27
39
|
if (!breakpoints) {
|
|
28
40
|
return newSizes;
|
|
29
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "5.
|
|
1
|
+
export declare const SDK_VERSION = "5.4.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "5.
|
|
1
|
+
export const SDK_VERSION = "5.4.0";
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
import { createBoundedCache } from '../../../helpers/bounded-cache';
|
|
1
2
|
import { flattenState, getFunctionArguments } from '../helpers';
|
|
3
|
+
const MAX_CACHED_FUNCTIONS = 5000;
|
|
4
|
+
const FUNCTION_CACHE = createBoundedCache(MAX_CACHED_FUNCTIONS);
|
|
5
|
+
// `getFunctionArguments` always returns the same argument names, so `code` alone is the key.
|
|
6
|
+
const getCompiledFunction = (argNames, code) => {
|
|
7
|
+
let fn = FUNCTION_CACHE.get(code);
|
|
8
|
+
if (!fn) {
|
|
9
|
+
fn = new Function(...argNames, code);
|
|
10
|
+
FUNCTION_CACHE.set(code, fn);
|
|
11
|
+
}
|
|
12
|
+
return fn;
|
|
13
|
+
};
|
|
2
14
|
export const runInBrowser = ({ code, builder, context, event, localState, rootSetState, rootState }) => {
|
|
3
15
|
const functionArgs = getFunctionArguments({
|
|
4
16
|
builder,
|
|
@@ -10,5 +22,5 @@ export const runInBrowser = ({ code, builder, context, event, localState, rootSe
|
|
|
10
22
|
rootSetState
|
|
11
23
|
})
|
|
12
24
|
});
|
|
13
|
-
return
|
|
25
|
+
return getCompiledFunction(functionArgs.map(([name]) => name), code)(...functionArgs.map(([, value]) => value));
|
|
14
26
|
};
|
|
@@ -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';
|
|
@@ -16,6 +17,24 @@ const VIRTUAL_INDEX_REGEX = /(\s)*var(\s)+_virtual_index(\s)*=(\s)*state(?<getPa
|
|
|
16
17
|
export const getSimpleExpressionGetPath = (code) => {
|
|
17
18
|
return STATE_GETTER_REGEX.exec(code.trim())?.groups?.getPath?.slice(1) || VIRTUAL_INDEX_REGEX.exec(code.trim())?.groups?.getPath?.slice(1);
|
|
18
19
|
};
|
|
20
|
+
const MAX_CACHED_CODE_STRINGS = 5000;
|
|
21
|
+
const SIMPLE_GET_PATH_CACHE = createBoundedCache(MAX_CACHED_CODE_STRINGS);
|
|
22
|
+
const getCachedSimpleExpressionGetPath = (code) => {
|
|
23
|
+
let getPath = SIMPLE_GET_PATH_CACHE.get(code);
|
|
24
|
+
if (getPath === undefined) {
|
|
25
|
+
getPath = getSimpleExpressionGetPath(code) || null;
|
|
26
|
+
SIMPLE_GET_PATH_CACHE.set(code, getPath);
|
|
27
|
+
}
|
|
28
|
+
return getPath;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Same result as `get({ ...rootState, ...localState }, getPath)`, without copying every state key.
|
|
32
|
+
*/
|
|
33
|
+
const getSimpleStateValue = (rootState, localState, getPath) => {
|
|
34
|
+
const firstKey = getPath.split('.')[0];
|
|
35
|
+
const source = localState && Object.prototype.hasOwnProperty.call(localState, firstKey) ? localState : rootState;
|
|
36
|
+
return get(source, getPath);
|
|
37
|
+
};
|
|
19
38
|
export function evaluate({ code, context, localState, rootState, rootSetState, event, isExpression = true, trackingContext }) {
|
|
20
39
|
if (code.trim() === '') {
|
|
21
40
|
return undefined;
|
|
@@ -26,12 +45,9 @@ export function evaluate({ code, context, localState, rootState, rootSetState, e
|
|
|
26
45
|
* We try not to take many risks with this optimizations, so we only do it for
|
|
27
46
|
* `state.{path}` expressions.
|
|
28
47
|
*/
|
|
29
|
-
const getPath =
|
|
48
|
+
const getPath = getCachedSimpleExpressionGetPath(code.trim());
|
|
30
49
|
if (getPath) {
|
|
31
|
-
return
|
|
32
|
-
...rootState,
|
|
33
|
-
...localState
|
|
34
|
-
}, getPath);
|
|
50
|
+
return getSimpleStateValue(rootState, localState, getPath);
|
|
35
51
|
}
|
|
36
52
|
const args = {
|
|
37
53
|
code: parseCode(code, {
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { SDK_NAME } from '../../../constants/sdk-name';
|
|
2
2
|
import { MSG_PREFIX, logger } from '../../../helpers/logger';
|
|
3
3
|
import { fastClone } from '../../fast-clone';
|
|
4
|
-
import { set } from '../../set';
|
|
4
|
+
import { set, unset } from '../../set';
|
|
5
5
|
import { getFunctionArguments } from '../helpers';
|
|
6
|
+
import { NOT_EVALUATED, evaluateSafeStateExpression } from '../safe-state-expression';
|
|
6
7
|
import { safeDynamicRequire } from './safeDynamicRequire';
|
|
7
8
|
const getSyncValName = (key) => `bldr_${key}_sync`;
|
|
8
9
|
const BUILDER_SET_STATE_NAME = 'BUILDER_SET_STATE';
|
|
10
|
+
const BUILDER_DELETE_STATE_NAME = 'BUILDER_DELETE_STATE';
|
|
9
11
|
const INJECTED_IVM_GLOBAL = 'BUILDER_IVM';
|
|
10
12
|
// Convert all argument references to proxies, and pass `copySync` method to target object, to return a copy of the original JS object
|
|
13
|
+
// Only the `state` proxy carries a `path`, so only writes and deletes on `state` reach the host's root state.
|
|
11
14
|
// https://github.com/laverdet/isolated-vm#referencecopysync
|
|
12
15
|
const REF_TO_PROXY_FN = `
|
|
13
|
-
var refToProxy = (obj) => {
|
|
16
|
+
var refToProxy = (obj, path) => {
|
|
14
17
|
if (typeof obj !== 'object' || obj === null) {
|
|
15
18
|
return obj;
|
|
16
19
|
}
|
|
@@ -21,25 +24,33 @@ var refToProxy = (obj) => {
|
|
|
21
24
|
}
|
|
22
25
|
const val = obj.getSync(key);
|
|
23
26
|
if (typeof val?.getSync === 'function') {
|
|
24
|
-
return refToProxy(val);
|
|
27
|
+
return refToProxy(val, path && path.concat(key));
|
|
25
28
|
}
|
|
26
29
|
return val;
|
|
27
30
|
},
|
|
28
31
|
set(target, key, value) {
|
|
32
|
+
// Functions cannot cross the isolate boundary, and server renders never call them.
|
|
33
|
+
if (typeof value === 'function') {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
29
36
|
const v = typeof value === 'object' ? new ${INJECTED_IVM_GLOBAL}.Reference(value) : value;
|
|
30
37
|
obj.setSync(key, v);
|
|
31
|
-
${BUILDER_SET_STATE_NAME}(key, value)
|
|
38
|
+
if (path) ${BUILDER_SET_STATE_NAME}(path.concat(key), value);
|
|
39
|
+
return true;
|
|
32
40
|
},
|
|
33
41
|
deleteProperty(target, key) {
|
|
34
42
|
obj.deleteSync(key);
|
|
43
|
+
if (path) ${BUILDER_DELETE_STATE_NAME}(path.concat(key));
|
|
44
|
+
return true;
|
|
35
45
|
}
|
|
36
46
|
})
|
|
37
47
|
}
|
|
38
48
|
`;
|
|
39
49
|
const processCode = ({ code, args }) => {
|
|
40
|
-
const fnArgs = args.map(([name]) => `var ${name} = refToProxy(${getSyncValName(name)}); `).join('');
|
|
50
|
+
const fnArgs = args.map(([name]) => `var ${name} = refToProxy(${getSyncValName(name)}${name === 'state' ? ', []' : ''}); `).join('');
|
|
41
51
|
// the output is stringified and parsed back to the parent isolate if needed (when it's an `object`)
|
|
42
|
-
|
|
52
|
+
// Wrapped in an IIFE so it compiles as a reusable script whose completion value is the result.
|
|
53
|
+
return `(function () {
|
|
43
54
|
${REF_TO_PROXY_FN}
|
|
44
55
|
${fnArgs}
|
|
45
56
|
function theFunction() {
|
|
@@ -53,13 +64,57 @@ if (typeof output === 'object' && output !== null) {
|
|
|
53
64
|
} else {
|
|
54
65
|
return output;
|
|
55
66
|
}
|
|
56
|
-
`;
|
|
67
|
+
})()`;
|
|
57
68
|
};
|
|
58
69
|
let IVM_INSTANCE = null;
|
|
59
|
-
// Create fresh isolates per request to prevent memory leaks
|
|
60
70
|
let IVM_OPTIONS = {
|
|
61
71
|
memoryLimit: 128
|
|
62
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Evaluations share one isolate, each in a fresh context. The isolate is disposed at the end of the
|
|
75
|
+
* current task (or after a fixed number of evaluations), so a render's bindings share it without it
|
|
76
|
+
* outliving that work: a single long-lived isolate leaked memory (#4210), and one still alive at
|
|
77
|
+
* worker-thread teardown crashes `isolated-vm` on Linux.
|
|
78
|
+
*/
|
|
79
|
+
const MAX_EVALUATIONS_PER_ISOLATE = 1000;
|
|
80
|
+
let POOLED = null;
|
|
81
|
+
let DISPOSE_TIMER = null;
|
|
82
|
+
let IS_EXIT_HANDLER_REGISTERED = false;
|
|
83
|
+
const disposePooledIsolate = () => {
|
|
84
|
+
if (DISPOSE_TIMER) {
|
|
85
|
+
clearTimeout(DISPOSE_TIMER);
|
|
86
|
+
DISPOSE_TIMER = null;
|
|
87
|
+
}
|
|
88
|
+
if (POOLED && !POOLED.isolate.isDisposed) {
|
|
89
|
+
try {
|
|
90
|
+
POOLED.isolate.dispose();
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
// Ignore disposal errors
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
POOLED = null;
|
|
97
|
+
};
|
|
98
|
+
const getPooledIsolate = (ivm) => {
|
|
99
|
+
if (!POOLED || POOLED.isolate.isDisposed || POOLED.evaluations >= MAX_EVALUATIONS_PER_ISOLATE) {
|
|
100
|
+
disposePooledIsolate();
|
|
101
|
+
POOLED = {
|
|
102
|
+
isolate: new ivm.Isolate(IVM_OPTIONS),
|
|
103
|
+
evaluations: 0,
|
|
104
|
+
scripts: new Map()
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (!DISPOSE_TIMER) {
|
|
108
|
+
DISPOSE_TIMER = setTimeout(disposePooledIsolate, 0);
|
|
109
|
+
DISPOSE_TIMER.unref?.();
|
|
110
|
+
}
|
|
111
|
+
if (!IS_EXIT_HANDLER_REGISTERED && typeof process !== 'undefined' && typeof process.once === 'function') {
|
|
112
|
+
IS_EXIT_HANDLER_REGISTERED = true;
|
|
113
|
+
process.once('exit', disposePooledIsolate);
|
|
114
|
+
}
|
|
115
|
+
POOLED.evaluations++;
|
|
116
|
+
return POOLED;
|
|
117
|
+
};
|
|
63
118
|
/**
|
|
64
119
|
* Set the `isolated-vm` instance to be used by the node runtime.
|
|
65
120
|
* This is useful for environments that are not able to rely on our
|
|
@@ -97,13 +152,34 @@ const getIvm = () => {
|
|
|
97
152
|
For more information, visit https://builder.io/c/docs/integration-tips#enabling-data-bindings-in-node-environments`;
|
|
98
153
|
throw new Error(ERROR_MESSAGE);
|
|
99
154
|
};
|
|
155
|
+
let IS_PROXY;
|
|
156
|
+
/**
|
|
157
|
+
* Node's `util.types.isProxy`, which detects a Proxy without running its traps.
|
|
158
|
+
* `null` when unavailable, in which case the pure-expression fast path is skipped.
|
|
159
|
+
*/
|
|
160
|
+
const getIsProxy = () => {
|
|
161
|
+
if (IS_PROXY === undefined) {
|
|
162
|
+
try {
|
|
163
|
+
IS_PROXY = safeDynamicRequire('node:util')?.types?.isProxy ?? null;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
IS_PROXY = null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return IS_PROXY;
|
|
170
|
+
};
|
|
100
171
|
export const runInNode = ({ code, builder, context, event, localState, rootSetState, rootState }) => {
|
|
172
|
+
const isProxy = getIsProxy();
|
|
173
|
+
if (isProxy) {
|
|
174
|
+
const safeValue = evaluateSafeStateExpression(code, rootState, localState, isProxy);
|
|
175
|
+
if (safeValue !== NOT_EVALUATED)
|
|
176
|
+
return safeValue;
|
|
177
|
+
}
|
|
101
178
|
const ivm = getIvm();
|
|
102
|
-
|
|
103
|
-
|
|
179
|
+
const pooled = getPooledIsolate(ivm);
|
|
180
|
+
const isolateContext = pooled.isolate.createContextSync();
|
|
181
|
+
const references = [];
|
|
104
182
|
try {
|
|
105
|
-
isolate = new ivm.Isolate(IVM_OPTIONS);
|
|
106
|
-
const isolateContext = isolate.createContextSync();
|
|
107
183
|
const jail = isolateContext.global;
|
|
108
184
|
// Setup the isolate
|
|
109
185
|
jail.setSync('global', jail.derefInto());
|
|
@@ -124,13 +200,17 @@ export const runInNode = ({ code, builder, context, event, localState, rootSetSt
|
|
|
124
200
|
/**
|
|
125
201
|
* Propagate state changes back to the reactive root state.
|
|
126
202
|
*/
|
|
127
|
-
jail.setSync(BUILDER_SET_STATE_NAME, function (
|
|
203
|
+
jail.setSync(BUILDER_SET_STATE_NAME, function (path, value) {
|
|
128
204
|
// mutate the `rootState` object itself. Important for cases where we do not have `rootSetState`
|
|
129
205
|
// like Qwik.
|
|
130
|
-
set(rootState,
|
|
206
|
+
set(rootState, path, value);
|
|
131
207
|
// call the `rootSetState` function if it exists
|
|
132
208
|
rootSetState?.(rootState);
|
|
133
209
|
});
|
|
210
|
+
jail.setSync(BUILDER_DELETE_STATE_NAME, function (path) {
|
|
211
|
+
unset(rootState, path);
|
|
212
|
+
rootSetState?.(rootState);
|
|
213
|
+
});
|
|
134
214
|
args.forEach(([key, arg]) => {
|
|
135
215
|
const val = typeof arg === 'object' ? new ivm.Reference(
|
|
136
216
|
// workaround: methods with default values for arguments is not being cloned over
|
|
@@ -138,13 +218,19 @@ export const runInNode = ({ code, builder, context, event, localState, rootSetSt
|
|
|
138
218
|
...arg,
|
|
139
219
|
getUserAttributes: () => arg.getUserAttributes()
|
|
140
220
|
} : arg) : null;
|
|
221
|
+
if (val)
|
|
222
|
+
references.push(val);
|
|
141
223
|
jail.setSync(getSyncValName(key), val);
|
|
142
224
|
});
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
225
|
+
let script = pooled.scripts.get(code);
|
|
226
|
+
if (!script) {
|
|
227
|
+
script = pooled.isolate.compileScriptSync(processCode({
|
|
228
|
+
code,
|
|
229
|
+
args
|
|
230
|
+
}));
|
|
231
|
+
pooled.scripts.set(code, script);
|
|
232
|
+
}
|
|
233
|
+
const resultStr = script.runSync(isolateContext);
|
|
148
234
|
try {
|
|
149
235
|
// returning objects throw errors in isolated vm, so we stringify it and parse it back
|
|
150
236
|
const res = JSON.parse(resultStr);
|
|
@@ -155,14 +241,19 @@ export const runInNode = ({ code, builder, context, event, localState, rootSetSt
|
|
|
155
241
|
}
|
|
156
242
|
}
|
|
157
243
|
finally {
|
|
158
|
-
|
|
159
|
-
if (isolate) {
|
|
244
|
+
references.forEach(reference => {
|
|
160
245
|
try {
|
|
161
|
-
|
|
246
|
+
reference.release();
|
|
162
247
|
}
|
|
163
248
|
catch (e) {
|
|
164
|
-
// Ignore
|
|
249
|
+
// Ignore release errors
|
|
165
250
|
}
|
|
251
|
+
});
|
|
252
|
+
try {
|
|
253
|
+
isolateContext.release();
|
|
254
|
+
}
|
|
255
|
+
catch (e) {
|
|
256
|
+
// Ignore release errors (e.g. the isolate was disposed after hitting its memory limit)
|
|
166
257
|
}
|
|
167
258
|
}
|
|
168
259
|
};
|