@builder.io/sdk-svelte 2.0.18 → 2.0.22
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/lib/browser/components/awaiter.svelte +4 -7
- package/lib/browser/components/awaiter.svelte.d.ts +3 -1
- package/lib/browser/components/content/components/enable-editor.svelte +9 -46
- package/lib/browser/components/content/content.svelte +25 -2
- package/lib/browser/components/content/content.svelte.d.ts +2 -2
- package/lib/browser/constants/sdk-version.d.ts +1 -1
- package/lib/browser/constants/sdk-version.js +1 -1
- package/lib/browser/context/component-reference-types.d.ts +6 -4
- package/lib/browser/functions/evaluate/browser-runtime/browser.d.ts +0 -6
- package/lib/browser/functions/evaluate/browser-runtime/browser.js +1 -30
- package/lib/browser/functions/evaluate/edge-runtime/edge-runtime.js +73 -34
- package/lib/browser/functions/evaluate/helpers.d.ts +5 -0
- package/lib/browser/functions/evaluate/helpers.js +29 -0
- package/lib/browser/types/input.d.ts +1 -1
- package/lib/edge/components/awaiter.svelte +4 -7
- package/lib/edge/components/awaiter.svelte.d.ts +3 -1
- package/lib/edge/components/content/components/enable-editor.svelte +9 -46
- package/lib/edge/components/content/content.svelte +25 -2
- package/lib/edge/components/content/content.svelte.d.ts +2 -2
- package/lib/edge/constants/sdk-version.d.ts +1 -1
- package/lib/edge/constants/sdk-version.js +1 -1
- package/lib/edge/context/component-reference-types.d.ts +6 -4
- package/lib/edge/functions/evaluate/browser-runtime/browser.d.ts +0 -6
- package/lib/edge/functions/evaluate/browser-runtime/browser.js +1 -30
- package/lib/edge/functions/evaluate/edge-runtime/edge-runtime.js +73 -34
- package/lib/edge/functions/evaluate/helpers.d.ts +5 -0
- package/lib/edge/functions/evaluate/helpers.js +29 -0
- package/lib/edge/types/input.d.ts +1 -1
- package/lib/node/components/awaiter.svelte +4 -7
- package/lib/node/components/awaiter.svelte.d.ts +3 -1
- package/lib/node/components/content/components/enable-editor.svelte +9 -46
- package/lib/node/components/content/content.svelte +25 -2
- package/lib/node/components/content/content.svelte.d.ts +2 -2
- package/lib/node/constants/sdk-version.d.ts +1 -1
- package/lib/node/constants/sdk-version.js +1 -1
- package/lib/node/context/component-reference-types.d.ts +6 -4
- package/lib/node/functions/evaluate/browser-runtime/browser.d.ts +0 -6
- package/lib/node/functions/evaluate/browser-runtime/browser.js +1 -30
- package/lib/node/functions/evaluate/edge-runtime/edge-runtime.js +73 -34
- package/lib/node/functions/evaluate/helpers.d.ts +5 -0
- package/lib/node/functions/evaluate/helpers.js +29 -0
- package/lib/node/types/input.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/awaiter.svelte +8 -8
- package/src/components/content/components/enable-editor.svelte +9 -52
- package/src/components/content/content.svelte +28 -2
- package/src/constants/sdk-version.ts +1 -1
- package/src/context/component-reference-types.ts +5 -3
- package/src/functions/evaluate/browser-runtime/browser.ts +1 -39
- package/src/functions/evaluate/edge-runtime/edge-runtime.ts +73 -35
- package/src/functions/evaluate/helpers.ts +37 -0
- package/src/types/input.ts +1 -1
|
@@ -1,32 +1,72 @@
|
|
|
1
1
|
import { logger } from '../../../helpers/logger';
|
|
2
|
-
import {
|
|
3
|
-
import { getFunctionArguments } from '../helpers';
|
|
2
|
+
import { flattenState, getFunctionArguments } from '../helpers';
|
|
4
3
|
import Interpreter from './acorn-interpreter';
|
|
4
|
+
/**
|
|
5
|
+
* https://stackoverflow.com/a/46503625
|
|
6
|
+
*/
|
|
7
|
+
function patchInterpreter() {
|
|
8
|
+
const originalGetProperty = Interpreter.prototype.getProperty;
|
|
9
|
+
const originalSetProperty = Interpreter.prototype.setProperty;
|
|
10
|
+
function newGetProperty(obj, name) {
|
|
11
|
+
if (obj == null || !obj._connected) {
|
|
12
|
+
return originalGetProperty.call(this, obj, name);
|
|
13
|
+
}
|
|
14
|
+
const value = obj._connected[name];
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return this.nativeToPseudo(value);
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === 'object') {
|
|
19
|
+
// if the value is an object itself, create another connected object
|
|
20
|
+
return this.createConnectedObject(value);
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function newSetProperty(obj, name, value, opt_descriptor) {
|
|
25
|
+
if (obj == null || !obj._connected) {
|
|
26
|
+
return originalSetProperty.call(this, obj, name, value, opt_descriptor);
|
|
27
|
+
}
|
|
28
|
+
obj._connected[name] = this.pseudoToNative(value);
|
|
29
|
+
}
|
|
30
|
+
const getKeys = [];
|
|
31
|
+
const setKeys = [];
|
|
32
|
+
for (const key of Object.keys(Interpreter.prototype)) {
|
|
33
|
+
if (Interpreter.prototype[key] === originalGetProperty) {
|
|
34
|
+
getKeys.push(key);
|
|
35
|
+
}
|
|
36
|
+
if (Interpreter.prototype[key] === originalSetProperty) {
|
|
37
|
+
setKeys.push(key);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const key of getKeys) {
|
|
41
|
+
Interpreter.prototype[key] = newGetProperty;
|
|
42
|
+
}
|
|
43
|
+
for (const key of setKeys) {
|
|
44
|
+
Interpreter.prototype[key] = newSetProperty;
|
|
45
|
+
}
|
|
46
|
+
Interpreter.prototype.createConnectedObject = function (obj) {
|
|
47
|
+
const connectedObject = this.createObject(this.OBJECT);
|
|
48
|
+
connectedObject._connected = obj;
|
|
49
|
+
return connectedObject;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
patchInterpreter();
|
|
5
53
|
const processCode = (code) => {
|
|
6
|
-
return code
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return line;
|
|
15
|
-
const [lhs, rhs] = trimmed.split('=');
|
|
16
|
-
const setStr = lhs.replace('state.', '').trim();
|
|
17
|
-
const setExpr = `setRootState('${setStr}', ${rhs.trim()})`;
|
|
18
|
-
return `
|
|
19
|
-
${line}
|
|
20
|
-
${setExpr}
|
|
21
|
-
`;
|
|
22
|
-
}).filter(Boolean).join('\n');
|
|
54
|
+
return code
|
|
55
|
+
// strip async/await polyfill: remove anything before 'function main()'
|
|
56
|
+
.replace(/^.*?function main\(\)/, `
|
|
57
|
+
var __awaiter = function (e, t, n, r) {return r()},
|
|
58
|
+
__generator = function (e, t) { return t() };
|
|
59
|
+
function main()`)
|
|
60
|
+
// replace ?. with .
|
|
61
|
+
.replace(/\?\./g, '.');
|
|
23
62
|
};
|
|
24
63
|
const getJSONValName = (val) => val + 'JSON';
|
|
25
64
|
export const runInEdge = ({ builder, context, event, rootState, localState, rootSetState, code }) => {
|
|
26
|
-
const state = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
65
|
+
const state = flattenState({
|
|
66
|
+
rootState,
|
|
67
|
+
localState,
|
|
68
|
+
rootSetState
|
|
69
|
+
});
|
|
30
70
|
const properties = getFunctionArguments({
|
|
31
71
|
builder,
|
|
32
72
|
context,
|
|
@@ -38,6 +78,9 @@ export const runInEdge = ({ builder, context, event, rootState, localState, root
|
|
|
38
78
|
*/
|
|
39
79
|
const prependedCode = properties.map(([key]) => {
|
|
40
80
|
const jsonValName = getJSONValName(key);
|
|
81
|
+
if (key === 'state') {
|
|
82
|
+
return ``;
|
|
83
|
+
}
|
|
41
84
|
return `var ${key} = ${jsonValName} === undefined ? undefined : JSON.parse(${jsonValName});`;
|
|
42
85
|
}).join('\n');
|
|
43
86
|
const cleanedCode = processCode(code);
|
|
@@ -53,23 +96,19 @@ function theFunction() {
|
|
|
53
96
|
}
|
|
54
97
|
theFunction();
|
|
55
98
|
`;
|
|
56
|
-
const setRootState = (prop, value) => {
|
|
57
|
-
const newState = set(state, prop, value);
|
|
58
|
-
rootSetState?.(newState);
|
|
59
|
-
};
|
|
60
99
|
const initFunc = function (interpreter, globalObject) {
|
|
61
100
|
/**
|
|
62
101
|
* serialize all function args to JSON strings
|
|
63
102
|
*/
|
|
64
103
|
properties.forEach(([key, val]) => {
|
|
65
|
-
|
|
66
|
-
|
|
104
|
+
if (key === 'state') {
|
|
105
|
+
interpreter.setProperty(globalObject, key, interpreter.createConnectedObject(val), interpreter.READONLY_DESCRIPTOR);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const jsonVal = JSON.stringify(val);
|
|
109
|
+
interpreter.setProperty(globalObject, getJSONValName(key), jsonVal);
|
|
110
|
+
}
|
|
67
111
|
});
|
|
68
|
-
/**
|
|
69
|
-
* Add a JavaScript function "setRootState" to the interpreter's global object, that will be called whenever a
|
|
70
|
-
* state property is set. This function will update the state object.
|
|
71
|
-
*/
|
|
72
|
-
interpreter.setProperty(globalObject, 'setRootState', interpreter.createNativeFunction(setRootState));
|
|
73
112
|
};
|
|
74
113
|
const myInterpreter = new Interpreter(transformed, initFunc);
|
|
75
114
|
myInterpreter.run();
|
|
@@ -23,3 +23,8 @@ export declare const getFunctionArguments: ({ builder, context, event, state }:
|
|
|
23
23
|
}) => [string, Event | BuilderRenderState | import("../../context/types").BuilderRenderContext | BuilderGlobals | undefined][];
|
|
24
24
|
export declare const getBuilderGlobals: () => BuilderGlobals;
|
|
25
25
|
export declare const parseCode: (code: string, { isExpression }: Pick<EvaluatorArgs, 'isExpression'>) => string;
|
|
26
|
+
export declare function flattenState({ rootState, localState, rootSetState }: {
|
|
27
|
+
rootState: Record<string | symbol, any>;
|
|
28
|
+
localState: Record<string | symbol, any> | undefined;
|
|
29
|
+
rootSetState: ((rootState: BuilderRenderState) => void) | undefined;
|
|
30
|
+
}): BuilderRenderState;
|
|
@@ -26,3 +26,32 @@ export const parseCode = (code, { isExpression = true }) => {
|
|
|
26
26
|
const useCode = useReturn ? `return (${code});` : code;
|
|
27
27
|
return useCode;
|
|
28
28
|
};
|
|
29
|
+
export function flattenState({ rootState, localState, rootSetState }) {
|
|
30
|
+
return new Proxy(rootState, {
|
|
31
|
+
get: (target, prop) => {
|
|
32
|
+
if (localState && prop in localState) {
|
|
33
|
+
return localState[prop];
|
|
34
|
+
}
|
|
35
|
+
const val = target[prop];
|
|
36
|
+
if (typeof val === 'object' && val !== null) {
|
|
37
|
+
return flattenState({
|
|
38
|
+
rootState: val,
|
|
39
|
+
localState: undefined,
|
|
40
|
+
rootSetState: rootSetState ? subState => {
|
|
41
|
+
target[prop] = subState;
|
|
42
|
+
rootSetState(target);
|
|
43
|
+
} : undefined
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return val;
|
|
47
|
+
},
|
|
48
|
+
set: (target, prop, value) => {
|
|
49
|
+
if (localState && prop in localState) {
|
|
50
|
+
throw new Error('Writing to local state is not allowed as it is read-only.');
|
|
51
|
+
}
|
|
52
|
+
target[prop] = value;
|
|
53
|
+
rootSetState?.(target);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -117,6 +117,6 @@ export interface Input {
|
|
|
117
117
|
valueType?: {
|
|
118
118
|
type?: string;
|
|
119
119
|
};
|
|
120
|
-
onChange?: ((options: Map<string, any>) => void | Promise<void>) | string;
|
|
120
|
+
onChange?: ((options: Map<string, any>, previousOptions?: Map<string, any>) => void | Promise<void>) | string;
|
|
121
121
|
meta?: Record<string, any>;
|
|
122
122
|
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
2
|
+
import type { SvelteComponent } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export let load: () => Promise<{ default: typeof SvelteComponent<any> }>;
|
|
3
5
|
export let fallback: any;
|
|
4
6
|
export let props: any;
|
|
5
7
|
export let attributes: any;
|
|
8
|
+
|
|
9
|
+
const componentImport = typeof load === 'string' ? import(load) : load();
|
|
6
10
|
</script>
|
|
7
11
|
|
|
8
|
-
{#await
|
|
12
|
+
{#await componentImport}
|
|
9
13
|
{#if fallback}
|
|
10
14
|
<svelte:component this={fallback} />
|
|
11
15
|
{/if}
|
|
12
|
-
|
|
13
|
-
<svelte:component
|
|
14
|
-
this={Component}
|
|
15
|
-
{...props}
|
|
16
|
-
attributes={attributes}
|
|
17
|
-
>
|
|
16
|
+
{:then { default: Component }}
|
|
17
|
+
<svelte:component this={Component} {...props} {attributes}>
|
|
18
18
|
<slot />
|
|
19
19
|
</svelte:component>
|
|
20
20
|
{/await}
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
import { evaluate } from "../../../functions/evaluate/index";
|
|
24
24
|
import { fastClone } from "../../../functions/fast-clone";
|
|
25
25
|
import { fetchOneEntry } from "../../../functions/get-content/index";
|
|
26
|
-
import { fetch } from "../../../functions/get-fetch";
|
|
27
26
|
import { isBrowser } from "../../../functions/is-browser";
|
|
28
27
|
import { isEditing } from "../../../functions/is-editing";
|
|
29
28
|
import { isPreviewing } from "../../../functions/is-previewing";
|
|
@@ -31,7 +30,6 @@
|
|
|
31
30
|
import { _track } from "../../../functions/track/index";
|
|
32
31
|
import { getInteractionPropertiesForEvent } from "../../../functions/track/interaction";
|
|
33
32
|
import { getDefaultCanTrack } from "../../../helpers/canTrack";
|
|
34
|
-
import { logger } from "../../../helpers/logger";
|
|
35
33
|
import { postPreviewContent } from "../../../helpers/preview-lru-cache/set";
|
|
36
34
|
import { createEditorListener } from "../../../helpers/subscribe-to-editor";
|
|
37
35
|
import {
|
|
@@ -121,23 +119,6 @@
|
|
|
121
119
|
},
|
|
122
120
|
})(event);
|
|
123
121
|
}
|
|
124
|
-
function evaluateJsCode() {
|
|
125
|
-
// run any dynamic JS code attached to content
|
|
126
|
-
const jsCode = $builderContextSignal.content?.data?.jsCode;
|
|
127
|
-
if (jsCode) {
|
|
128
|
-
evaluate({
|
|
129
|
-
code: jsCode,
|
|
130
|
-
context: context || {},
|
|
131
|
-
localState: undefined,
|
|
132
|
-
rootState: $builderContextSignal.rootState,
|
|
133
|
-
rootSetState: $builderContextSignal.rootSetState,
|
|
134
|
-
/**
|
|
135
|
-
* We don't want to cache the result of the JS code, since it's arbitrary side effect code.
|
|
136
|
-
*/
|
|
137
|
-
enableCache: false,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
122
|
function onClick(event: any) {
|
|
142
123
|
if ($builderContextSignal.content) {
|
|
143
124
|
const variationId = $builderContextSignal.content?.testVariationId;
|
|
@@ -294,6 +275,8 @@
|
|
|
294
275
|
let httpReqsData = {};
|
|
295
276
|
let httpReqsPending = {};
|
|
296
277
|
let clicked = false;
|
|
278
|
+
runHttpRequests();
|
|
279
|
+
emitStateUpdate();
|
|
297
280
|
|
|
298
281
|
onMount(() => {
|
|
299
282
|
if (isBrowser()) {
|
|
@@ -328,16 +311,6 @@
|
|
|
328
311
|
}
|
|
329
312
|
}
|
|
330
313
|
});
|
|
331
|
-
onMount(() => {
|
|
332
|
-
if (!apiKey) {
|
|
333
|
-
logger.error(
|
|
334
|
-
"No API key provided to `Content` component. This can cause issues. Please provide an API key using the `apiKey` prop."
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
evaluateJsCode();
|
|
338
|
-
runHttpRequests();
|
|
339
|
-
emitStateUpdate();
|
|
340
|
-
});
|
|
341
314
|
|
|
342
315
|
function onUpdateFn_0(..._args: any[]) {
|
|
343
316
|
if (content) {
|
|
@@ -348,37 +321,21 @@
|
|
|
348
321
|
$: onUpdateFn_0(...[content]);
|
|
349
322
|
|
|
350
323
|
function onUpdateFn_1(..._args: any[]) {
|
|
351
|
-
evaluateJsCode();
|
|
352
|
-
}
|
|
353
|
-
$: onUpdateFn_1_builderContextSignal_content__data__jsCode =
|
|
354
|
-
$builderContextSignal.content?.data?.jsCode;
|
|
355
|
-
$: onUpdateFn_1(...[onUpdateFn_1_builderContextSignal_content__data__jsCode]);
|
|
356
|
-
|
|
357
|
-
function onUpdateFn_2(..._args: any[]) {
|
|
358
|
-
runHttpRequests();
|
|
359
|
-
}
|
|
360
|
-
$: onUpdateFn_2_builderContextSignal_content__data__httpRequests =
|
|
361
|
-
$builderContextSignal.content?.data?.httpRequests;
|
|
362
|
-
$: onUpdateFn_2(
|
|
363
|
-
...[onUpdateFn_2_builderContextSignal_content__data__httpRequests]
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
function onUpdateFn_3(..._args: any[]) {
|
|
367
324
|
emitStateUpdate();
|
|
368
325
|
}
|
|
369
|
-
$:
|
|
326
|
+
$: onUpdateFn_1_builderContextSignal_rootState =
|
|
370
327
|
$builderContextSignal.rootState;
|
|
371
|
-
$:
|
|
328
|
+
$: onUpdateFn_1(...[onUpdateFn_1_builderContextSignal_rootState]);
|
|
372
329
|
|
|
373
|
-
function
|
|
330
|
+
function onUpdateFn_2(..._args: any[]) {
|
|
374
331
|
if (data) {
|
|
375
332
|
mergeNewRootState(data);
|
|
376
333
|
}
|
|
377
334
|
}
|
|
378
335
|
|
|
379
|
-
$:
|
|
336
|
+
$: onUpdateFn_2(...[data]);
|
|
380
337
|
|
|
381
|
-
function
|
|
338
|
+
function onUpdateFn_3(..._args: any[]) {
|
|
382
339
|
if (locale) {
|
|
383
340
|
mergeNewRootState({
|
|
384
341
|
locale: locale,
|
|
@@ -386,7 +343,7 @@
|
|
|
386
343
|
}
|
|
387
344
|
}
|
|
388
345
|
|
|
389
|
-
$:
|
|
346
|
+
$: onUpdateFn_3(...[locale]);
|
|
390
347
|
|
|
391
348
|
setContext(builderContext.key, builderContextSignal);
|
|
392
349
|
|
|
@@ -411,10 +368,10 @@
|
|
|
411
368
|
}}
|
|
412
369
|
builder-content-id={$builderContextSignal.content?.id}
|
|
413
370
|
builder-model={model}
|
|
371
|
+
className={getWrapperClassName(content?.testVariationId || content?.id)}
|
|
414
372
|
{...{}}
|
|
415
373
|
{...showContentProps()}
|
|
416
374
|
{...contentWrapperProps}
|
|
417
|
-
class={getWrapperClassName(content?.testVariationId || content?.id)}
|
|
418
375
|
on:initeditingbldr={(event) => {
|
|
419
376
|
elementRef_onIniteditingbldr(event);
|
|
420
377
|
}}
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
BuilderRenderState,
|
|
11
11
|
RegisteredComponents,
|
|
12
12
|
} from "../../context/types";
|
|
13
|
+
import { evaluate } from "../../functions/evaluate/evaluate";
|
|
13
14
|
import { serializeIncludingFunctions } from "../../functions/register-component";
|
|
15
|
+
import { logger } from "../../helpers/logger";
|
|
14
16
|
import type { ComponentInfo } from "../../types/components";
|
|
15
17
|
import type { Dictionary } from "../../types/typescript";
|
|
16
18
|
import Blocks from "../blocks/blocks.svelte";
|
|
@@ -26,14 +28,14 @@
|
|
|
26
28
|
import type { ContentProps } from "./content.types";
|
|
27
29
|
import { wrapComponentRef } from "./wrap-component-ref";
|
|
28
30
|
|
|
31
|
+
export let apiKey: ContentProps["apiKey"];
|
|
32
|
+
export let context: ContentProps["context"] = undefined;
|
|
29
33
|
export let content: ContentProps["content"] = undefined;
|
|
30
34
|
export let customComponents: ContentProps["customComponents"] = undefined;
|
|
31
35
|
export let model: ContentProps["model"] = undefined;
|
|
32
36
|
export let data: ContentProps["data"] = undefined;
|
|
33
37
|
export let locale: ContentProps["locale"] = undefined;
|
|
34
|
-
export let context: ContentProps["context"] = undefined;
|
|
35
38
|
export let canTrack: ContentProps["canTrack"] = undefined;
|
|
36
|
-
export let apiKey: ContentProps["apiKey"];
|
|
37
39
|
export let apiVersion: ContentProps["apiVersion"] = undefined;
|
|
38
40
|
export let blocksWrapper: ContentProps["blocksWrapper"] = undefined;
|
|
39
41
|
export let blocksWrapperProps: ContentProps["blocksWrapperProps"] = undefined;
|
|
@@ -110,6 +112,30 @@
|
|
|
110
112
|
BlocksWrapperProps: blocksWrapperProps || {},
|
|
111
113
|
nonce: nonce || "",
|
|
112
114
|
});
|
|
115
|
+
if (!apiKey) {
|
|
116
|
+
logger.error(
|
|
117
|
+
"No API key provided to `Content` component. This can cause issues. Please provide an API key using the `apiKey` prop."
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// run any dynamic JS code attached to content
|
|
122
|
+
const jsCode = $builderContextSignal.content?.data?.jsCode;
|
|
123
|
+
if (jsCode) {
|
|
124
|
+
evaluate({
|
|
125
|
+
code: jsCode,
|
|
126
|
+
context: context || {},
|
|
127
|
+
localState: undefined,
|
|
128
|
+
rootState: $builderContextSignal.rootState,
|
|
129
|
+
rootSetState: (newState) => {
|
|
130
|
+
$builderContextSignal.rootSetState?.(newState);
|
|
131
|
+
},
|
|
132
|
+
isExpression: false,
|
|
133
|
+
/**
|
|
134
|
+
* We don't want to cache the result of the JS code, since it's arbitrary side effect code.
|
|
135
|
+
*/
|
|
136
|
+
enableCache: false,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
113
139
|
|
|
114
140
|
setContext(ComponentsContext.key, {
|
|
115
141
|
registeredComponents: registeredComponents,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "2.0.
|
|
1
|
+
export const SDK_VERSION = "2.0.22"
|
|
@@ -3,11 +3,13 @@ import type { SvelteComponent } from 'svelte';
|
|
|
3
3
|
export type ComponentReference =
|
|
4
4
|
| {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Either:
|
|
7
|
+
* - A dynamic import function pointing to the component.
|
|
7
8
|
* Example: `() => import('../components/my-component.svelte')`
|
|
8
|
-
*
|
|
9
|
+
* - A string representing the path to the component.
|
|
10
|
+
* Example: `'../components/my-component.svelte'`
|
|
9
11
|
*/
|
|
10
|
-
load: () => Promise<{ default: typeof SvelteComponent<any> }
|
|
12
|
+
load: string | (() => Promise<{ default: typeof SvelteComponent<any> }>);
|
|
11
13
|
/**
|
|
12
14
|
* An optional fallback component to render while the dynamic component is loading.
|
|
13
15
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { BuilderRenderState } from '../../../context/types';
|
|
2
1
|
import type { ExecutorArgs } from '../helpers';
|
|
3
|
-
import { getFunctionArguments } from '../helpers';
|
|
2
|
+
import { flattenState, getFunctionArguments } from '../helpers';
|
|
4
3
|
export const runInBrowser = ({
|
|
5
4
|
code,
|
|
6
5
|
builder,
|
|
@@ -21,41 +20,4 @@ export const runInBrowser = ({
|
|
|
21
20
|
})
|
|
22
21
|
});
|
|
23
22
|
return new Function(...functionArgs.map(([name]) => name), code)(...functionArgs.map(([, value]) => value));
|
|
24
|
-
};
|
|
25
|
-
export function flattenState({
|
|
26
|
-
rootState,
|
|
27
|
-
localState,
|
|
28
|
-
rootSetState
|
|
29
|
-
}: {
|
|
30
|
-
rootState: Record<string | symbol, any>;
|
|
31
|
-
localState: Record<string | symbol, any> | undefined;
|
|
32
|
-
rootSetState: ((rootState: BuilderRenderState) => void) | undefined;
|
|
33
|
-
}): BuilderRenderState {
|
|
34
|
-
return new Proxy(rootState, {
|
|
35
|
-
get: (target, prop) => {
|
|
36
|
-
if (localState && prop in localState) {
|
|
37
|
-
return localState[prop];
|
|
38
|
-
}
|
|
39
|
-
const val = target[prop];
|
|
40
|
-
if (typeof val === 'object' && val !== null) {
|
|
41
|
-
return flattenState({
|
|
42
|
-
rootState: val,
|
|
43
|
-
localState: undefined,
|
|
44
|
-
rootSetState: rootSetState ? subState => {
|
|
45
|
-
target[prop] = subState;
|
|
46
|
-
rootSetState(target);
|
|
47
|
-
} : undefined
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return val;
|
|
51
|
-
},
|
|
52
|
-
set: (target, prop, value) => {
|
|
53
|
-
if (localState && prop in localState) {
|
|
54
|
-
throw new Error('Writing to local state is not allowed as it is read-only.');
|
|
55
|
-
}
|
|
56
|
-
target[prop] = value;
|
|
57
|
-
rootSetState?.(target);
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
23
|
}
|
|
@@ -1,26 +1,66 @@
|
|
|
1
1
|
import { logger } from '../../../helpers/logger';
|
|
2
|
-
import { set } from '../../set';
|
|
3
2
|
import type { ExecutorArgs } from '../helpers';
|
|
4
|
-
import { getFunctionArguments } from '../helpers';
|
|
3
|
+
import { flattenState, getFunctionArguments } from '../helpers';
|
|
5
4
|
import Interpreter from './acorn-interpreter';
|
|
6
|
-
const processCode = (code: string) => {
|
|
7
|
-
return code.split('\n').map(line => {
|
|
8
|
-
const trimmed = line.trim();
|
|
9
|
-
|
|
10
|
-
// this async wrapper doesn't work in JS-interpreter, so we drop it.
|
|
11
|
-
if (line.includes('__awaiter')) return undefined;
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
6
|
+
/**
|
|
7
|
+
* https://stackoverflow.com/a/46503625
|
|
8
|
+
*/
|
|
9
|
+
function patchInterpreter() {
|
|
10
|
+
const originalGetProperty = Interpreter.prototype.getProperty;
|
|
11
|
+
const originalSetProperty = Interpreter.prototype.setProperty;
|
|
12
|
+
function newGetProperty(this: typeof Interpreter, obj: any, name: any) {
|
|
13
|
+
if (obj == null || !obj._connected) {
|
|
14
|
+
return originalGetProperty.call(this, obj, name);
|
|
15
|
+
}
|
|
16
|
+
const value = obj._connected[name];
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
return this.nativeToPseudo(value);
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === 'object') {
|
|
21
|
+
// if the value is an object itself, create another connected object
|
|
22
|
+
return this.createConnectedObject(value);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function newSetProperty(this: typeof Interpreter, obj: any, name: any, value: any, opt_descriptor: any) {
|
|
27
|
+
if (obj == null || !obj._connected) {
|
|
28
|
+
return originalSetProperty.call(this, obj, name, value, opt_descriptor);
|
|
29
|
+
}
|
|
30
|
+
obj._connected[name] = this.pseudoToNative(value);
|
|
31
|
+
}
|
|
32
|
+
const getKeys: string[] = [];
|
|
33
|
+
const setKeys: string[] = [];
|
|
34
|
+
for (const key of Object.keys(Interpreter.prototype)) {
|
|
35
|
+
if (Interpreter.prototype[key] === originalGetProperty) {
|
|
36
|
+
getKeys.push(key);
|
|
37
|
+
}
|
|
38
|
+
if (Interpreter.prototype[key] === originalSetProperty) {
|
|
39
|
+
setKeys.push(key);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const key of getKeys) {
|
|
43
|
+
Interpreter.prototype[key] = newGetProperty;
|
|
44
|
+
}
|
|
45
|
+
for (const key of setKeys) {
|
|
46
|
+
Interpreter.prototype[key] = newSetProperty;
|
|
47
|
+
}
|
|
48
|
+
Interpreter.prototype.createConnectedObject = function (obj: any) {
|
|
49
|
+
const connectedObject = this.createObject(this.OBJECT);
|
|
50
|
+
connectedObject._connected = obj;
|
|
51
|
+
return connectedObject;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
patchInterpreter();
|
|
55
|
+
const processCode = (code: string) => {
|
|
56
|
+
return code
|
|
57
|
+
// strip async/await polyfill: remove anything before 'function main()'
|
|
58
|
+
.replace(/^.*?function main\(\)/, `
|
|
59
|
+
var __awaiter = function (e, t, n, r) {return r()},
|
|
60
|
+
__generator = function (e, t) { return t() };
|
|
61
|
+
function main()`)
|
|
62
|
+
// replace ?. with .
|
|
63
|
+
.replace(/\?\./g, '.');
|
|
24
64
|
};
|
|
25
65
|
const getJSONValName = (val: string) => val + 'JSON';
|
|
26
66
|
export const runInEdge = ({
|
|
@@ -32,10 +72,11 @@ export const runInEdge = ({
|
|
|
32
72
|
rootSetState,
|
|
33
73
|
code
|
|
34
74
|
}: ExecutorArgs) => {
|
|
35
|
-
const state = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
75
|
+
const state = flattenState({
|
|
76
|
+
rootState,
|
|
77
|
+
localState,
|
|
78
|
+
rootSetState
|
|
79
|
+
});
|
|
39
80
|
const properties = getFunctionArguments({
|
|
40
81
|
builder,
|
|
41
82
|
context,
|
|
@@ -48,6 +89,9 @@ export const runInEdge = ({
|
|
|
48
89
|
*/
|
|
49
90
|
const prependedCode = properties.map(([key]) => {
|
|
50
91
|
const jsonValName = getJSONValName(key);
|
|
92
|
+
if (key === 'state') {
|
|
93
|
+
return ``;
|
|
94
|
+
}
|
|
51
95
|
return `var ${key} = ${jsonValName} === undefined ? undefined : JSON.parse(${jsonValName});`;
|
|
52
96
|
}).join('\n');
|
|
53
97
|
const cleanedCode = processCode(code);
|
|
@@ -63,24 +107,18 @@ function theFunction() {
|
|
|
63
107
|
}
|
|
64
108
|
theFunction();
|
|
65
109
|
`;
|
|
66
|
-
const setRootState = (prop: string, value: any) => {
|
|
67
|
-
const newState = set(state, prop, value);
|
|
68
|
-
rootSetState?.(newState);
|
|
69
|
-
};
|
|
70
110
|
const initFunc = function (interpreter: any, globalObject: any) {
|
|
71
111
|
/**
|
|
72
112
|
* serialize all function args to JSON strings
|
|
73
113
|
*/
|
|
74
114
|
properties.forEach(([key, val]) => {
|
|
75
|
-
|
|
76
|
-
|
|
115
|
+
if (key === 'state') {
|
|
116
|
+
interpreter.setProperty(globalObject, key, interpreter.createConnectedObject(val), interpreter.READONLY_DESCRIPTOR);
|
|
117
|
+
} else {
|
|
118
|
+
const jsonVal = JSON.stringify(val);
|
|
119
|
+
interpreter.setProperty(globalObject, getJSONValName(key), jsonVal);
|
|
120
|
+
}
|
|
77
121
|
});
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Add a JavaScript function "setRootState" to the interpreter's global object, that will be called whenever a
|
|
81
|
-
* state property is set. This function will update the state object.
|
|
82
|
-
*/
|
|
83
|
-
interpreter.setProperty(globalObject, 'setRootState', interpreter.createNativeFunction(setRootState));
|
|
84
122
|
};
|
|
85
123
|
const myInterpreter = new Interpreter(transformed, initFunc);
|
|
86
124
|
myInterpreter.run();
|
|
@@ -53,4 +53,41 @@ export const parseCode = (code: string, {
|
|
|
53
53
|
isExpression && !(code.includes(';') || code.includes(' return ') || code.trim().startsWith('return '));
|
|
54
54
|
const useCode = useReturn ? `return (${code});` : code;
|
|
55
55
|
return useCode;
|
|
56
|
+
};
|
|
57
|
+
export function flattenState({
|
|
58
|
+
rootState,
|
|
59
|
+
localState,
|
|
60
|
+
rootSetState
|
|
61
|
+
}: {
|
|
62
|
+
rootState: Record<string | symbol, any>;
|
|
63
|
+
localState: Record<string | symbol, any> | undefined;
|
|
64
|
+
rootSetState: ((rootState: BuilderRenderState) => void) | undefined;
|
|
65
|
+
}): BuilderRenderState {
|
|
66
|
+
return new Proxy(rootState, {
|
|
67
|
+
get: (target, prop) => {
|
|
68
|
+
if (localState && prop in localState) {
|
|
69
|
+
return localState[prop];
|
|
70
|
+
}
|
|
71
|
+
const val = target[prop];
|
|
72
|
+
if (typeof val === 'object' && val !== null) {
|
|
73
|
+
return flattenState({
|
|
74
|
+
rootState: val,
|
|
75
|
+
localState: undefined,
|
|
76
|
+
rootSetState: rootSetState ? subState => {
|
|
77
|
+
target[prop] = subState;
|
|
78
|
+
rootSetState(target);
|
|
79
|
+
} : undefined
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return val;
|
|
83
|
+
},
|
|
84
|
+
set: (target, prop, value) => {
|
|
85
|
+
if (localState && prop in localState) {
|
|
86
|
+
throw new Error('Writing to local state is not allowed as it is read-only.');
|
|
87
|
+
}
|
|
88
|
+
target[prop] = value;
|
|
89
|
+
rootSetState?.(target);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
56
93
|
}
|
package/src/types/input.ts
CHANGED
|
@@ -118,6 +118,6 @@ export interface Input {
|
|
|
118
118
|
valueType?: {
|
|
119
119
|
type?: string;
|
|
120
120
|
};
|
|
121
|
-
onChange?: ((options: Map<string, any>) => void | Promise<void>) | string;
|
|
121
|
+
onChange?: ((options: Map<string, any>, previousOptions?: Map<string, any>) => void | Promise<void>) | string;
|
|
122
122
|
meta?: Record<string, any>;
|
|
123
123
|
}
|