mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-11 14:20:07 +08:00
fix(operators): Remove deprecated _js wasm.
This commit is contained in:
parent
70707b4ec0
commit
20d2d0f0d9
@ -16,126 +16,14 @@
|
||||
|
||||
import { type } from '@lowdefy/helpers';
|
||||
|
||||
// ! ---------------
|
||||
// ! DEPRECATED
|
||||
// ! ---------------
|
||||
import { serializer } from '@lowdefy/helpers';
|
||||
import { getQuickJS, shouldInterruptAfterDeadline } from 'quickjs-emscripten';
|
||||
let QuickJsVm;
|
||||
|
||||
function createFunction({ params, location, methodName }) {
|
||||
if (!params.code || !type.isString(params.code)) {
|
||||
throw new Error(
|
||||
`Operator Error: _js.${methodName} "code" argument should be a string at ${location}.`
|
||||
);
|
||||
}
|
||||
const fn = (...args) => {
|
||||
const jsFnString = `
|
||||
var logs = [];
|
||||
function log(...item) {
|
||||
logs.push(item);
|
||||
}
|
||||
var console = {
|
||||
log,
|
||||
};
|
||||
function dateReviver(key, value) {
|
||||
if (typeof value === 'object' && value !== null && value.hasOwnProperty('_date')) {
|
||||
var date = new Date(value._date);
|
||||
if (date instanceof Date && !Number.isNaN(date.getTime())) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function dateSerialize(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
Object.keys(value).forEach((k) => {
|
||||
if (value[k] instanceof Date && !Number.isNaN(value[k].getTime())) {
|
||||
value[k] = { "_date": value[k].valueOf() };
|
||||
}
|
||||
})
|
||||
}
|
||||
return value;
|
||||
}
|
||||
var args = JSON.parse(decodeURIComponent("${encodeURIComponent(
|
||||
serializer.serializeToString(args)
|
||||
)}"), dateReviver);
|
||||
var fnResult = (${params.code})(...(args || []));
|
||||
var result = encodeURIComponent(JSON.stringify([fnResult, logs], dateSerialize));
|
||||
`;
|
||||
const codeHandle = QuickJsVm.unwrapResult(
|
||||
QuickJsVm.evalCode(jsFnString, {
|
||||
shouldInterrupt: shouldInterruptAfterDeadline(Date.now() + 1000),
|
||||
memoryLimitBytes: 1024 * 1024,
|
||||
})
|
||||
);
|
||||
const resultHandle = QuickJsVm.getProp(QuickJsVm.global, 'result');
|
||||
codeHandle.dispose();
|
||||
const result = serializer.deserializeFromString(
|
||||
decodeURIComponent(QuickJsVm.getString(resultHandle))
|
||||
);
|
||||
result[1].forEach((item) => {
|
||||
console.log(...item);
|
||||
});
|
||||
return result[0];
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
function evaluate({ params, location, methodName }) {
|
||||
if (!type.isArray(params.args) && !type.isNone(params.args)) {
|
||||
throw new Error(
|
||||
`Operator Error: _js.evaluate "args" argument should be an array, null or undefined at ${location}.`
|
||||
);
|
||||
}
|
||||
const fn = createFunction({ params, location, methodName });
|
||||
return fn(...(params.args || []));
|
||||
}
|
||||
|
||||
const methods = { evaluate, function: createFunction };
|
||||
|
||||
function _DEPRECATED_js({ params, location, methodName }) {
|
||||
if (!QuickJsVm) {
|
||||
throw new Error(
|
||||
`Operator Error: _js is not initialized. Received: ${JSON.stringify(params)} at ${location}.`
|
||||
);
|
||||
}
|
||||
|
||||
return methods[methodName]({ params, location, methodName });
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const QuickJs = await getQuickJS();
|
||||
QuickJsVm = QuickJs.createVm();
|
||||
}
|
||||
async function clear() {
|
||||
QuickJsVm = null;
|
||||
}
|
||||
|
||||
_js.init = init;
|
||||
_js.clear = clear;
|
||||
// ! ---------------
|
||||
|
||||
function _js({ context, params, location, methodName }) {
|
||||
// ! DEPRECATED methods
|
||||
if (!type.isFunction(context.lowdefy.imports.jsOperators[methodName]) && !methods[methodName]) {
|
||||
if (!type.isFunction(context.lowdefy.imports.jsOperators[methodName])) {
|
||||
throw new Error(`Operator Error: _js.${methodName} is not a function.`);
|
||||
}
|
||||
if (context.lowdefy.imports.jsOperators[methodName]) {
|
||||
if (!type.isNone(params) && !type.isArray(params)) {
|
||||
throw new Error(`Operator Error: _js.${methodName} takes an array as input at ${location}.`);
|
||||
}
|
||||
return context.lowdefy.imports.jsOperators[methodName](...(params || []));
|
||||
if (!type.isNone(params) && !type.isArray(params)) {
|
||||
throw new Error(`Operator Error: _js.${methodName} takes an array as input at ${location}.`);
|
||||
}
|
||||
// ! DEPRECATED ---------------
|
||||
console.warn(
|
||||
'WARNING: _js.evaluate and _js.function will has been deprecated and will be removed in the next version. Please see: https://docs.lowdefy.com/_js for more details.'
|
||||
);
|
||||
if (!type.isObject(params)) {
|
||||
throw new Error(`Operator Error: _js.${methodName} takes an object as input at ${location}.`);
|
||||
}
|
||||
return _DEPRECATED_js({ params, location, methodName });
|
||||
// ! ---------------
|
||||
return context.lowdefy.imports.jsOperators[methodName](...(params || []));
|
||||
}
|
||||
|
||||
export default _js;
|
||||
|
@ -499,19 +499,18 @@ describe('parse operators', () => {
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test('parse _js operator', async () => {
|
||||
test.only('parse _js operator retuning a function', async () => {
|
||||
const test_fn = () => (a, b) => a + b;
|
||||
const mockFn = jest.fn().mockImplementation(test_fn);
|
||||
context.lowdefy.imports.jsOperators.test_fn = mockFn;
|
||||
const input = {
|
||||
'_js.function': {
|
||||
code: `function (a, b){
|
||||
return a + b;
|
||||
}`,
|
||||
},
|
||||
'_js.test_fn': [],
|
||||
};
|
||||
const parser = new WebParser({ context, contexts });
|
||||
await parser.init();
|
||||
const { output, errors } = parser.parse({ input, location: 'locationId' });
|
||||
expect(output).toBeInstanceOf(Function);
|
||||
expect(output(1, 2)).toEqual(3);
|
||||
expect(output(4, 2)).toEqual(6);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
|
@ -18,9 +18,6 @@ import _js from '../../src/web/js';
|
||||
import { context } from '../testContext';
|
||||
|
||||
const location = 'location';
|
||||
beforeAll(async () => {
|
||||
await _js.init();
|
||||
});
|
||||
|
||||
test('_js.test_fn and params to return a value', () => {
|
||||
const params = [12, 14];
|
||||
@ -59,302 +56,12 @@ test('_js.test_fn params not an array', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// ! --------------
|
||||
// ! DEPRECATED
|
||||
// ! --------------
|
||||
test('_js with code and args specified', () => {
|
||||
const params = {
|
||||
code: `function (one, two) {
|
||||
return one + two;
|
||||
}`,
|
||||
args: [12, 14],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
expect(fn(1, 2)).toEqual(3);
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual(26);
|
||||
expect(_js({ context, location, params: { code: params.code }, methodName: 'evaluate' })).toEqual(
|
||||
null
|
||||
test('_js.not_a_function', () => {
|
||||
const params = 10;
|
||||
const test_fn = (a, b) => a + b;
|
||||
const mockFn = jest.fn().mockImplementation(test_fn);
|
||||
context.lowdefy.imports.jsOperators.test_fn = mockFn;
|
||||
expect(() => _js({ context, location, params, methodName: 'not_a_function' })).toThrow(
|
||||
new Error('Operator Error: _js.not_a_function is not a function.')
|
||||
);
|
||||
});
|
||||
|
||||
test('_js with code and args specified to return json object', () => {
|
||||
const params = {
|
||||
code: `function (one, two) {
|
||||
return { a: one, b: two, c: [1,2,3, one, two, "three"]};
|
||||
}`,
|
||||
args: [12, 14],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
expect(fn(1, 2)).toEqual({ a: 1, b: 2, c: [1, 2, 3, 1, 2, 'three'] });
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual({
|
||||
a: 12,
|
||||
b: 14,
|
||||
c: [1, 2, 3, 12, 14, 'three'],
|
||||
});
|
||||
expect(_js({ context, location, params: { code: params.code }, methodName: 'evaluate' })).toEqual(
|
||||
{
|
||||
c: [1, 2, 3, null, null, 'three'],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('_js with code and args specified to return json array', () => {
|
||||
const params = {
|
||||
code: `function (one, two) {
|
||||
return [1,2,3, one, two, "three"];
|
||||
}`,
|
||||
args: [12, 14],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
expect(fn(1, 2)).toEqual([1, 2, 3, 1, 2, 'three']);
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual([
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
12,
|
||||
14,
|
||||
'three',
|
||||
]);
|
||||
expect(_js({ context, location, params: { code: params.code }, methodName: 'evaluate' })).toEqual(
|
||||
[1, 2, 3, null, null, 'three']
|
||||
);
|
||||
});
|
||||
|
||||
test('_js with open "\'" in result', () => {
|
||||
const str = `<div style=\"test: one;\">one's result</div>`;
|
||||
const params = {
|
||||
code: `function chars(input) {
|
||||
return [{x: input, b: 1}];
|
||||
}`,
|
||||
args: [str],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
expect(fn(str)).toEqual([{ x: str, b: 1 }]);
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual([{ x: str, b: 1 }]);
|
||||
});
|
||||
|
||||
test('_js with date in input and result', () => {
|
||||
const params = {
|
||||
code: `function duration(from, to) {
|
||||
return {
|
||||
from,
|
||||
to,
|
||||
fromIsDate: from instanceof Date,
|
||||
toIsDate: from instanceof Date,
|
||||
duration: to - from,
|
||||
};
|
||||
}`,
|
||||
args: [new Date(0), new Date(10)],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual({
|
||||
from: new Date(0),
|
||||
to: new Date(10),
|
||||
duration: 10,
|
||||
fromIsDate: true,
|
||||
toIsDate: true,
|
||||
});
|
||||
expect(fn(new Date(0), new Date(10))).toEqual({
|
||||
from: new Date(0),
|
||||
to: new Date(10),
|
||||
duration: 10,
|
||||
fromIsDate: true,
|
||||
toIsDate: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('_js with undefined result returns null', () => {
|
||||
const params = {
|
||||
code: `function add(one, two) {
|
||||
return;
|
||||
}`,
|
||||
args: [12, 14],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
expect(fn()).toEqual(null);
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual(null);
|
||||
expect(_js({ context, location, params: { code: params.code }, methodName: 'evaluate' })).toEqual(
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
test('_js with console.log', () => {
|
||||
const logger = console.log;
|
||||
console.log = jest.fn();
|
||||
const params = {
|
||||
code: `function logTest(one, two) {
|
||||
console.log(one)
|
||||
console.log(two)
|
||||
console.log(one, two, null, 123, 'abc', true, false);
|
||||
return;
|
||||
}`,
|
||||
args: [12, new Date(1)],
|
||||
};
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
_js({ context, location, params, methodName: 'evaluate' });
|
||||
expect(console.log.mock.calls).toEqual([
|
||||
[12],
|
||||
[new Date(1)],
|
||||
[12, new Date(1), null, 123, 'abc', true, false],
|
||||
]);
|
||||
console.log = logger;
|
||||
});
|
||||
|
||||
test('_js with code with args that needs escaped characters', () => {
|
||||
const params = {
|
||||
code: `function (obj) {
|
||||
return "<div>" + obj.html + "</html>";
|
||||
}`,
|
||||
args: [
|
||||
{
|
||||
html: '<a href="https://lowdefy.com">Lowdefy Website</a>',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(_js({ context, location, params, methodName: 'evaluate' })).toEqual(
|
||||
'<div><a href="https://lowdefy.com">Lowdefy Website</a></html>'
|
||||
);
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(fn).toBeInstanceOf(Function);
|
||||
expect(
|
||||
fn({
|
||||
html: '<a href="https://lowdefy.com">Lowdefy Website</a>',
|
||||
})
|
||||
).toEqual('<div><a href="https://lowdefy.com">Lowdefy Website</a></html>');
|
||||
});
|
||||
|
||||
test('_js with code and no "function" specified to throw', () => {
|
||||
const params = {
|
||||
code: `
|
||||
var value = "world";
|
||||
var result = 'a new ' + 'vm for Hello ' + value;
|
||||
`,
|
||||
};
|
||||
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(`"unexpected token in expression: 'var'"`);
|
||||
expect(() => {
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
fn();
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"unexpected token in expression: 'var'"`);
|
||||
});
|
||||
|
||||
test('_js invalid js code', () => {
|
||||
const params = {
|
||||
code: 'Hello',
|
||||
};
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(`"'Hello' is not defined"`);
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(() => fn()).toThrowErrorMatchingInlineSnapshot(`"'Hello' is not defined"`);
|
||||
});
|
||||
|
||||
test('_js not a function', () => {
|
||||
const params = {
|
||||
code: '"Hello"',
|
||||
};
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(`"not a function"`);
|
||||
const fn = _js({ context, location, params, methodName: 'function' });
|
||||
expect(() => fn()).toThrowErrorMatchingInlineSnapshot(`"not a function"`);
|
||||
});
|
||||
|
||||
test('_js params not a object', () => {
|
||||
const params = [];
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'function' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.function takes an object as input at location."`
|
||||
);
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.evaluate takes an object as input at location."`
|
||||
);
|
||||
});
|
||||
|
||||
test('_js.invalid methodName', () => {
|
||||
const params = {
|
||||
code: `function () {
|
||||
return args[0] + args[1]
|
||||
}`,
|
||||
};
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'invalid' })
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Operator Error: _js.invalid is not a function."`);
|
||||
});
|
||||
|
||||
test('_js invalid js code', () => {
|
||||
const params = {
|
||||
code: 1,
|
||||
};
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'function' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.function \\"code\\" argument should be a string at location."`
|
||||
);
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.evaluate \\"code\\" argument should be a string at location."`
|
||||
);
|
||||
});
|
||||
|
||||
test('_js no body or file', () => {
|
||||
const params = {};
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'function' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.function \\"code\\" argument should be a string at location."`
|
||||
);
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.evaluate \\"code\\" argument should be a string at location."`
|
||||
);
|
||||
});
|
||||
|
||||
test('_js.evaluate, args not an array', () => {
|
||||
const params = {
|
||||
args: 1,
|
||||
body: `function () {
|
||||
return args[0] + args[1]
|
||||
}`,
|
||||
};
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js.evaluate \\"args\\" argument should be an array, null or undefined at location."`
|
||||
);
|
||||
});
|
||||
|
||||
test('_js with undefined vm', () => {
|
||||
const params = {
|
||||
code: `{
|
||||
return args[0] + args[1]
|
||||
}`,
|
||||
};
|
||||
_js.clear();
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'function' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js is not initialized. Received: {\\"code\\":\\"{\\\\n return args[0] + args[1]\\\\n }\\"} at location."`
|
||||
);
|
||||
expect(() =>
|
||||
_js({ context, location, params, methodName: 'evaluate' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Operator Error: _js is not initialized. Received: {\\"code\\":\\"{\\\\n return args[0] + args[1]\\\\n }\\"} at location."`
|
||||
);
|
||||
});
|
||||
// ! --------------
|
||||
|
Loading…
Reference in New Issue
Block a user