|
|
@@ -0,0 +1,916 @@
|
|
|
+/**
|
|
|
+ * @node code
|
|
|
+ * @name Code
|
|
|
+ * @category core
|
|
|
+ * @version 1.0.0
|
|
|
+ * @description Execute custom JavaScript code with access to input data and helper utilities
|
|
|
+ * @icon code
|
|
|
+ */
|
|
|
+
|
|
|
+const configSchema = {
|
|
|
+ type: 'object',
|
|
|
+ properties: {
|
|
|
+ code: {
|
|
|
+ type: 'string',
|
|
|
+ title: 'JavaScript Code',
|
|
|
+ description: 'Custom JavaScript code to execute. Access input data via "input", workflow context via "context", and helper utilities via "_" (lodash-like). Return a value to set the node output.',
|
|
|
+ format: 'code',
|
|
|
+ default: '// Access input data with: input\n// Access workflow context with: context\n// Use helper utilities with: _ (underscore)\n\n// Example: Transform input data\nconst result = {\n processed: true,\n data: input.data,\n timestamp: _.now()\n};\n\nreturn result;'
|
|
|
+ },
|
|
|
+ timeout: {
|
|
|
+ type: 'number',
|
|
|
+ title: 'Timeout (seconds)',
|
|
|
+ description: 'Maximum execution time in seconds (1-300). Default: 30 seconds.',
|
|
|
+ default: 30,
|
|
|
+ minimum: 1,
|
|
|
+ maximum: 300
|
|
|
+ }
|
|
|
+ },
|
|
|
+ required: ['code']
|
|
|
+};
|
|
|
+
|
|
|
+const inputSchema = {
|
|
|
+ type: 'object',
|
|
|
+ properties: {
|
|
|
+ data: {
|
|
|
+ type: 'any',
|
|
|
+ description: 'Input data available as "input" in your code'
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const outputSchema = {
|
|
|
+ type: 'object',
|
|
|
+ properties: {
|
|
|
+ result: {
|
|
|
+ type: 'any',
|
|
|
+ description: 'The value returned by your code'
|
|
|
+ },
|
|
|
+ executionTime: {
|
|
|
+ type: 'number',
|
|
|
+ description: 'Execution time in milliseconds'
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+function createHelpers() {
|
|
|
+ return {
|
|
|
+ now: function() {
|
|
|
+ return Date.now();
|
|
|
+ },
|
|
|
+
|
|
|
+ isString: function(value) {
|
|
|
+ return typeof value === 'string';
|
|
|
+ },
|
|
|
+
|
|
|
+ isNumber: function(value) {
|
|
|
+ return typeof value === 'number' && !isNaN(value);
|
|
|
+ },
|
|
|
+
|
|
|
+ isBoolean: function(value) {
|
|
|
+ return typeof value === 'boolean';
|
|
|
+ },
|
|
|
+
|
|
|
+ isArray: function(value) {
|
|
|
+ return Array.isArray(value);
|
|
|
+ },
|
|
|
+
|
|
|
+ isObject: function(value) {
|
|
|
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
|
+ },
|
|
|
+
|
|
|
+ isNull: function(value) {
|
|
|
+ return value === null;
|
|
|
+ },
|
|
|
+
|
|
|
+ isUndefined: function(value) {
|
|
|
+ return value === undefined;
|
|
|
+ },
|
|
|
+
|
|
|
+ isNil: function(value) {
|
|
|
+ return value === null || value === undefined;
|
|
|
+ },
|
|
|
+
|
|
|
+ isEmpty: function(value) {
|
|
|
+ if (value === null || value === undefined) return true;
|
|
|
+ if (typeof value === 'string' || Array.isArray(value)) return value.length === 0;
|
|
|
+ if (typeof value === 'object') return Object.keys(value).length === 0;
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+
|
|
|
+ get: function(obj, path, defaultValue) {
|
|
|
+ if (obj === null || obj === undefined) return defaultValue;
|
|
|
+ var keys = typeof path === 'string' ? path.split('.') : path;
|
|
|
+ var result = obj;
|
|
|
+ for (var i = 0; i < keys.length; i++) {
|
|
|
+ if (result === null || result === undefined) return defaultValue;
|
|
|
+ result = result[keys[i]];
|
|
|
+ }
|
|
|
+ return result === undefined ? defaultValue : result;
|
|
|
+ },
|
|
|
+
|
|
|
+ set: function(obj, path, value) {
|
|
|
+ if (obj === null || typeof obj !== 'object') return obj;
|
|
|
+ var keys = typeof path === 'string' ? path.split('.') : path;
|
|
|
+ var current = obj;
|
|
|
+ for (var i = 0; i < keys.length - 1; i++) {
|
|
|
+ var key = keys[i];
|
|
|
+ if (current[key] === undefined || current[key] === null) {
|
|
|
+ current[key] = {};
|
|
|
+ }
|
|
|
+ current = current[key];
|
|
|
+ }
|
|
|
+ current[keys[keys.length - 1]] = value;
|
|
|
+ return obj;
|
|
|
+ },
|
|
|
+
|
|
|
+ has: function(obj, path) {
|
|
|
+ if (obj === null || obj === undefined) return false;
|
|
|
+ var keys = typeof path === 'string' ? path.split('.') : path;
|
|
|
+ var current = obj;
|
|
|
+ for (var i = 0; i < keys.length; i++) {
|
|
|
+ if (current === null || current === undefined) return false;
|
|
|
+ if (!Object.prototype.hasOwnProperty.call(current, keys[i])) return false;
|
|
|
+ current = current[keys[i]];
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+
|
|
|
+ keys: function(obj) {
|
|
|
+ if (obj === null || typeof obj !== 'object') return [];
|
|
|
+ return Object.keys(obj);
|
|
|
+ },
|
|
|
+
|
|
|
+ values: function(obj) {
|
|
|
+ if (obj === null || typeof obj !== 'object') return [];
|
|
|
+ return Object.values(obj);
|
|
|
+ },
|
|
|
+
|
|
|
+ entries: function(obj) {
|
|
|
+ if (obj === null || typeof obj !== 'object') return [];
|
|
|
+ return Object.entries(obj);
|
|
|
+ },
|
|
|
+
|
|
|
+ pick: function(obj, keys) {
|
|
|
+ if (obj === null || typeof obj !== 'object') return {};
|
|
|
+ var result = {};
|
|
|
+ for (var i = 0; i < keys.length; i++) {
|
|
|
+ var key = keys[i];
|
|
|
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
|
+ result[key] = obj[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ omit: function(obj, keys) {
|
|
|
+ if (obj === null || typeof obj !== 'object') return {};
|
|
|
+ var result = {};
|
|
|
+ var keysSet = {};
|
|
|
+ for (var i = 0; i < keys.length; i++) {
|
|
|
+ keysSet[keys[i]] = true;
|
|
|
+ }
|
|
|
+ for (var key in obj) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(obj, key) && !keysSet[key]) {
|
|
|
+ result[key] = obj[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ merge: function() {
|
|
|
+ var result = {};
|
|
|
+ for (var i = 0; i < arguments.length; i++) {
|
|
|
+ var obj = arguments[i];
|
|
|
+ if (obj !== null && typeof obj === 'object') {
|
|
|
+ for (var key in obj) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
|
+ result[key] = obj[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ deepMerge: function(target, source) {
|
|
|
+ if (source === null || typeof source !== 'object') return target;
|
|
|
+ if (target === null || typeof target !== 'object') return source;
|
|
|
+
|
|
|
+ var result = Array.isArray(target) ? [] : {};
|
|
|
+
|
|
|
+ for (var key in target) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
|
+ result[key] = target[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var key in source) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
|
+ if (source[key] !== null && typeof source[key] === 'object' &&
|
|
|
+ result[key] !== null && typeof result[key] === 'object') {
|
|
|
+ result[key] = this.deepMerge(result[key], source[key]);
|
|
|
+ } else {
|
|
|
+ result[key] = source[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ clone: function(value) {
|
|
|
+ if (value === null || typeof value !== 'object') return value;
|
|
|
+ return JSON.parse(JSON.stringify(value));
|
|
|
+ },
|
|
|
+
|
|
|
+ map: function(collection, iteratee) {
|
|
|
+ if (collection === null || collection === undefined) return [];
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.map(iteratee);
|
|
|
+ }
|
|
|
+ return Object.keys(collection).map(function(key) {
|
|
|
+ return iteratee(collection[key], key);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ filter: function(collection, predicate) {
|
|
|
+ if (collection === null || collection === undefined) return [];
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.filter(predicate);
|
|
|
+ }
|
|
|
+ var result = [];
|
|
|
+ for (var key in collection) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(collection, key)) {
|
|
|
+ if (predicate(collection[key], key)) {
|
|
|
+ result.push(collection[key]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ find: function(collection, predicate) {
|
|
|
+ if (collection === null || collection === undefined) return undefined;
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.find(predicate);
|
|
|
+ }
|
|
|
+ for (var key in collection) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(collection, key)) {
|
|
|
+ if (predicate(collection[key], key)) {
|
|
|
+ return collection[key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return undefined;
|
|
|
+ },
|
|
|
+
|
|
|
+ findIndex: function(array, predicate) {
|
|
|
+ if (!Array.isArray(array)) return -1;
|
|
|
+ return array.findIndex(predicate);
|
|
|
+ },
|
|
|
+
|
|
|
+ reduce: function(collection, iteratee, accumulator) {
|
|
|
+ if (collection === null || collection === undefined) return accumulator;
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.reduce(iteratee, accumulator);
|
|
|
+ }
|
|
|
+ var result = accumulator;
|
|
|
+ for (var key in collection) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(collection, key)) {
|
|
|
+ result = iteratee(result, collection[key], key);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ forEach: function(collection, iteratee) {
|
|
|
+ if (collection === null || collection === undefined) return;
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ collection.forEach(iteratee);
|
|
|
+ } else {
|
|
|
+ for (var key in collection) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(collection, key)) {
|
|
|
+ iteratee(collection[key], key);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ some: function(collection, predicate) {
|
|
|
+ if (collection === null || collection === undefined) return false;
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.some(predicate);
|
|
|
+ }
|
|
|
+ for (var key in collection) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(collection, key)) {
|
|
|
+ if (predicate(collection[key], key)) return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+
|
|
|
+ every: function(collection, predicate) {
|
|
|
+ if (collection === null || collection === undefined) return true;
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.every(predicate);
|
|
|
+ }
|
|
|
+ for (var key in collection) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(collection, key)) {
|
|
|
+ if (!predicate(collection[key], key)) return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+
|
|
|
+ includes: function(collection, value) {
|
|
|
+ if (collection === null || collection === undefined) return false;
|
|
|
+ if (Array.isArray(collection)) {
|
|
|
+ return collection.includes(value);
|
|
|
+ }
|
|
|
+ if (typeof collection === 'string') {
|
|
|
+ return collection.includes(value);
|
|
|
+ }
|
|
|
+ return Object.values(collection).includes(value);
|
|
|
+ },
|
|
|
+
|
|
|
+ groupBy: function(collection, key) {
|
|
|
+ if (collection === null || collection === undefined) return {};
|
|
|
+ var result = {};
|
|
|
+ var arr = Array.isArray(collection) ? collection : Object.values(collection);
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ var item = arr[i];
|
|
|
+ var groupKey = typeof key === 'function' ? key(item) : item[key];
|
|
|
+ if (!result[groupKey]) {
|
|
|
+ result[groupKey] = [];
|
|
|
+ }
|
|
|
+ result[groupKey].push(item);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ keyBy: function(collection, key) {
|
|
|
+ if (collection === null || collection === undefined) return {};
|
|
|
+ var result = {};
|
|
|
+ var arr = Array.isArray(collection) ? collection : Object.values(collection);
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ var item = arr[i];
|
|
|
+ var itemKey = typeof key === 'function' ? key(item) : item[key];
|
|
|
+ result[itemKey] = item;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ sortBy: function(collection, key) {
|
|
|
+ if (!Array.isArray(collection)) return [];
|
|
|
+ return collection.slice().sort(function(a, b) {
|
|
|
+ var aVal = typeof key === 'function' ? key(a) : a[key];
|
|
|
+ var bVal = typeof key === 'function' ? key(b) : b[key];
|
|
|
+ if (aVal < bVal) return -1;
|
|
|
+ if (aVal > bVal) return 1;
|
|
|
+ return 0;
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ uniq: function(array) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ var seen = [];
|
|
|
+ var result = [];
|
|
|
+ for (var i = 0; i < array.length; i++) {
|
|
|
+ var item = array[i];
|
|
|
+ var found = false;
|
|
|
+ for (var j = 0; j < seen.length; j++) {
|
|
|
+ if (JSON.stringify(seen[j]) === JSON.stringify(item)) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!found) {
|
|
|
+ seen.push(item);
|
|
|
+ result.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ uniqBy: function(array, key) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ var seen = {};
|
|
|
+ var result = [];
|
|
|
+ for (var i = 0; i < array.length; i++) {
|
|
|
+ var item = array[i];
|
|
|
+ var itemKey = typeof key === 'function' ? key(item) : item[key];
|
|
|
+ if (!seen[itemKey]) {
|
|
|
+ seen[itemKey] = true;
|
|
|
+ result.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ flatten: function(array) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ var result = [];
|
|
|
+ for (var i = 0; i < array.length; i++) {
|
|
|
+ if (Array.isArray(array[i])) {
|
|
|
+ for (var j = 0; j < array[i].length; j++) {
|
|
|
+ result.push(array[i][j]);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ result.push(array[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ flattenDeep: function(array) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ var result = [];
|
|
|
+ function flattenRecursive(arr) {
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ if (Array.isArray(arr[i])) {
|
|
|
+ flattenRecursive(arr[i]);
|
|
|
+ } else {
|
|
|
+ result.push(arr[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ flattenRecursive(array);
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ compact: function(array) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ return array.filter(function(item) {
|
|
|
+ return Boolean(item);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ first: function(array) {
|
|
|
+ if (!Array.isArray(array) || array.length === 0) return undefined;
|
|
|
+ return array[0];
|
|
|
+ },
|
|
|
+
|
|
|
+ last: function(array) {
|
|
|
+ if (!Array.isArray(array) || array.length === 0) return undefined;
|
|
|
+ return array[array.length - 1];
|
|
|
+ },
|
|
|
+
|
|
|
+ take: function(array, n) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ n = n === undefined ? 1 : n;
|
|
|
+ return array.slice(0, n);
|
|
|
+ },
|
|
|
+
|
|
|
+ drop: function(array, n) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ n = n === undefined ? 1 : n;
|
|
|
+ return array.slice(n);
|
|
|
+ },
|
|
|
+
|
|
|
+ chunk: function(array, size) {
|
|
|
+ if (!Array.isArray(array) || size < 1) return [];
|
|
|
+ var result = [];
|
|
|
+ for (var i = 0; i < array.length; i += size) {
|
|
|
+ result.push(array.slice(i, i + size));
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ reverse: function(array) {
|
|
|
+ if (!Array.isArray(array)) return [];
|
|
|
+ return array.slice().reverse();
|
|
|
+ },
|
|
|
+
|
|
|
+ trim: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str.trim();
|
|
|
+ },
|
|
|
+
|
|
|
+ trimStart: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str.replace(/^\s+/, '');
|
|
|
+ },
|
|
|
+
|
|
|
+ trimEnd: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str.replace(/\s+$/, '');
|
|
|
+ },
|
|
|
+
|
|
|
+ toUpper: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str.toUpperCase();
|
|
|
+ },
|
|
|
+
|
|
|
+ toLower: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str.toLowerCase();
|
|
|
+ },
|
|
|
+
|
|
|
+ capitalize: function(str) {
|
|
|
+ if (typeof str !== 'string' || str.length === 0) return '';
|
|
|
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
|
+ },
|
|
|
+
|
|
|
+ camelCase: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str
|
|
|
+ .replace(/[-_\s]+(.)?/g, function(_, char) {
|
|
|
+ return char ? char.toUpperCase() : '';
|
|
|
+ })
|
|
|
+ .replace(/^(.)/, function(firstChar) {
|
|
|
+ return firstChar.toLowerCase();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ snakeCase: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str
|
|
|
+ .replace(/([A-Z])/g, '_$1')
|
|
|
+ .replace(/[-\s]+/g, '_')
|
|
|
+ .toLowerCase()
|
|
|
+ .replace(/^_/, '');
|
|
|
+ },
|
|
|
+
|
|
|
+ kebabCase: function(str) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str
|
|
|
+ .replace(/([A-Z])/g, '-$1')
|
|
|
+ .replace(/[_\s]+/g, '-')
|
|
|
+ .toLowerCase()
|
|
|
+ .replace(/^-/, '');
|
|
|
+ },
|
|
|
+
|
|
|
+ startsWith: function(str, target) {
|
|
|
+ if (typeof str !== 'string') return false;
|
|
|
+ return str.indexOf(target) === 0;
|
|
|
+ },
|
|
|
+
|
|
|
+ endsWith: function(str, target) {
|
|
|
+ if (typeof str !== 'string') return false;
|
|
|
+ return str.indexOf(target, str.length - target.length) !== -1;
|
|
|
+ },
|
|
|
+
|
|
|
+ split: function(str, separator, limit) {
|
|
|
+ if (typeof str !== 'string') return [];
|
|
|
+ return str.split(separator, limit);
|
|
|
+ },
|
|
|
+
|
|
|
+ join: function(array, separator) {
|
|
|
+ if (!Array.isArray(array)) return '';
|
|
|
+ return array.join(separator === undefined ? ',' : separator);
|
|
|
+ },
|
|
|
+
|
|
|
+ replace: function(str, pattern, replacement) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ return str.replace(pattern, replacement);
|
|
|
+ },
|
|
|
+
|
|
|
+ repeat: function(str, n) {
|
|
|
+ if (typeof str !== 'string' || n < 1) return '';
|
|
|
+ var result = '';
|
|
|
+ for (var i = 0; i < n; i++) {
|
|
|
+ result += str;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ padStart: function(str, length, chars) {
|
|
|
+ if (typeof str !== 'string') str = String(str);
|
|
|
+ chars = chars === undefined ? ' ' : chars;
|
|
|
+ while (str.length < length) {
|
|
|
+ str = chars + str;
|
|
|
+ }
|
|
|
+ return str.slice(-length);
|
|
|
+ },
|
|
|
+
|
|
|
+ padEnd: function(str, length, chars) {
|
|
|
+ if (typeof str !== 'string') str = String(str);
|
|
|
+ chars = chars === undefined ? ' ' : chars;
|
|
|
+ while (str.length < length) {
|
|
|
+ str = str + chars;
|
|
|
+ }
|
|
|
+ return str.slice(0, length);
|
|
|
+ },
|
|
|
+
|
|
|
+ truncate: function(str, options) {
|
|
|
+ if (typeof str !== 'string') return '';
|
|
|
+ options = options || {};
|
|
|
+ var length = options.length || 30;
|
|
|
+ var omission = options.omission || '...';
|
|
|
+ if (str.length <= length) return str;
|
|
|
+ return str.slice(0, length - omission.length) + omission;
|
|
|
+ },
|
|
|
+
|
|
|
+ parseInt: function(str, radix) {
|
|
|
+ return parseInt(str, radix || 10);
|
|
|
+ },
|
|
|
+
|
|
|
+ parseFloat: function(str) {
|
|
|
+ return parseFloat(str);
|
|
|
+ },
|
|
|
+
|
|
|
+ toNumber: function(value) {
|
|
|
+ if (typeof value === 'number') return value;
|
|
|
+ if (typeof value === 'string') {
|
|
|
+ var parsed = parseFloat(value);
|
|
|
+ return isNaN(parsed) ? 0 : parsed;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ },
|
|
|
+
|
|
|
+ toString: function(value) {
|
|
|
+ if (value === null) return 'null';
|
|
|
+ if (value === undefined) return 'undefined';
|
|
|
+ return String(value);
|
|
|
+ },
|
|
|
+
|
|
|
+ toJson: function(value, pretty) {
|
|
|
+ return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
|
|
|
+ },
|
|
|
+
|
|
|
+ parseJson: function(str) {
|
|
|
+ try {
|
|
|
+ return JSON.parse(str);
|
|
|
+ } catch (e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ min: function(array) {
|
|
|
+ if (!Array.isArray(array) || array.length === 0) return undefined;
|
|
|
+ return Math.min.apply(null, array);
|
|
|
+ },
|
|
|
+
|
|
|
+ max: function(array) {
|
|
|
+ if (!Array.isArray(array) || array.length === 0) return undefined;
|
|
|
+ return Math.max.apply(null, array);
|
|
|
+ },
|
|
|
+
|
|
|
+ sum: function(array) {
|
|
|
+ if (!Array.isArray(array)) return 0;
|
|
|
+ return array.reduce(function(acc, val) {
|
|
|
+ return acc + (typeof val === 'number' ? val : 0);
|
|
|
+ }, 0);
|
|
|
+ },
|
|
|
+
|
|
|
+ mean: function(array) {
|
|
|
+ if (!Array.isArray(array) || array.length === 0) return NaN;
|
|
|
+ return this.sum(array) / array.length;
|
|
|
+ },
|
|
|
+
|
|
|
+ round: function(number, precision) {
|
|
|
+ precision = precision || 0;
|
|
|
+ var factor = Math.pow(10, precision);
|
|
|
+ return Math.round(number * factor) / factor;
|
|
|
+ },
|
|
|
+
|
|
|
+ floor: function(number, precision) {
|
|
|
+ precision = precision || 0;
|
|
|
+ var factor = Math.pow(10, precision);
|
|
|
+ return Math.floor(number * factor) / factor;
|
|
|
+ },
|
|
|
+
|
|
|
+ ceil: function(number, precision) {
|
|
|
+ precision = precision || 0;
|
|
|
+ var factor = Math.pow(10, precision);
|
|
|
+ return Math.ceil(number * factor) / factor;
|
|
|
+ },
|
|
|
+
|
|
|
+ clamp: function(number, lower, upper) {
|
|
|
+ if (number < lower) return lower;
|
|
|
+ if (number > upper) return upper;
|
|
|
+ return number;
|
|
|
+ },
|
|
|
+
|
|
|
+ random: function(lower, upper) {
|
|
|
+ if (upper === undefined) {
|
|
|
+ upper = lower;
|
|
|
+ lower = 0;
|
|
|
+ }
|
|
|
+ return lower + Math.floor(Math.random() * (upper - lower + 1));
|
|
|
+ },
|
|
|
+
|
|
|
+ range: function(start, end, step) {
|
|
|
+ if (end === undefined) {
|
|
|
+ end = start;
|
|
|
+ start = 0;
|
|
|
+ }
|
|
|
+ step = step || 1;
|
|
|
+ var result = [];
|
|
|
+ if (step > 0) {
|
|
|
+ for (var i = start; i < end; i += step) {
|
|
|
+ result.push(i);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (var i = start; i > end; i += step) {
|
|
|
+ result.push(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ formatDate: function(date, format) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ if (isNaN(date.getTime())) return '';
|
|
|
+
|
|
|
+ format = format || 'YYYY-MM-DD HH:mm:ss';
|
|
|
+
|
|
|
+ var year = date.getFullYear();
|
|
|
+ var month = date.getMonth() + 1;
|
|
|
+ var day = date.getDate();
|
|
|
+ var hours = date.getHours();
|
|
|
+ var minutes = date.getMinutes();
|
|
|
+ var seconds = date.getSeconds();
|
|
|
+ var ms = date.getMilliseconds();
|
|
|
+
|
|
|
+ return format
|
|
|
+ .replace('YYYY', year)
|
|
|
+ .replace('YY', String(year).slice(-2))
|
|
|
+ .replace('MM', this.padStart(month, 2, '0'))
|
|
|
+ .replace('M', month)
|
|
|
+ .replace('DD', this.padStart(day, 2, '0'))
|
|
|
+ .replace('D', day)
|
|
|
+ .replace('HH', this.padStart(hours, 2, '0'))
|
|
|
+ .replace('H', hours)
|
|
|
+ .replace('mm', this.padStart(minutes, 2, '0'))
|
|
|
+ .replace('m', minutes)
|
|
|
+ .replace('ss', this.padStart(seconds, 2, '0'))
|
|
|
+ .replace('s', seconds)
|
|
|
+ .replace('SSS', this.padStart(ms, 3, '0'));
|
|
|
+ },
|
|
|
+
|
|
|
+ parseDate: function(str) {
|
|
|
+ var date = new Date(str);
|
|
|
+ return isNaN(date.getTime()) ? null : date;
|
|
|
+ },
|
|
|
+
|
|
|
+ addDays: function(date, days) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ var result = new Date(date);
|
|
|
+ result.setDate(result.getDate() + days);
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ addHours: function(date, hours) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ var result = new Date(date);
|
|
|
+ result.setTime(result.getTime() + hours * 60 * 60 * 1000);
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ addMinutes: function(date, minutes) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ var result = new Date(date);
|
|
|
+ result.setTime(result.getTime() + minutes * 60 * 1000);
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ diffDays: function(date1, date2) {
|
|
|
+ if (!(date1 instanceof Date)) date1 = new Date(date1);
|
|
|
+ if (!(date2 instanceof Date)) date2 = new Date(date2);
|
|
|
+ var diff = date2.getTime() - date1.getTime();
|
|
|
+ return Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
|
+ },
|
|
|
+
|
|
|
+ diffHours: function(date1, date2) {
|
|
|
+ if (!(date1 instanceof Date)) date1 = new Date(date1);
|
|
|
+ if (!(date2 instanceof Date)) date2 = new Date(date2);
|
|
|
+ var diff = date2.getTime() - date1.getTime();
|
|
|
+ return Math.floor(diff / (1000 * 60 * 60));
|
|
|
+ },
|
|
|
+
|
|
|
+ startOfDay: function(date) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ var result = new Date(date);
|
|
|
+ result.setHours(0, 0, 0, 0);
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ endOfDay: function(date) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ var result = new Date(date);
|
|
|
+ result.setHours(23, 59, 59, 999);
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+
|
|
|
+ isoString: function(date) {
|
|
|
+ if (!(date instanceof Date)) {
|
|
|
+ date = new Date(date);
|
|
|
+ }
|
|
|
+ return date.toISOString();
|
|
|
+ },
|
|
|
+
|
|
|
+ uuid: function() {
|
|
|
+ return smartbotic.utils.uuid();
|
|
|
+ },
|
|
|
+
|
|
|
+ base64Encode: function(str) {
|
|
|
+ return smartbotic.utils.base64Encode(str);
|
|
|
+ },
|
|
|
+
|
|
|
+ base64Decode: function(str) {
|
|
|
+ return smartbotic.utils.base64Decode(str);
|
|
|
+ },
|
|
|
+
|
|
|
+ sha256: function(str) {
|
|
|
+ return smartbotic.utils.sha256(str);
|
|
|
+ },
|
|
|
+
|
|
|
+ sleep: function(ms) {
|
|
|
+ return smartbotic.utils.sleep(ms);
|
|
|
+ }
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function extractLineNumber(error) {
|
|
|
+ if (!error || !error.stack) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var stack = error.stack.toString();
|
|
|
+ var lines = stack.split('\n');
|
|
|
+
|
|
|
+ for (var i = 0; i < lines.length; i++) {
|
|
|
+ var line = lines[i];
|
|
|
+
|
|
|
+ var evalMatch = line.match(/at\s+eval[^:]*:(\d+):/);
|
|
|
+ if (evalMatch) {
|
|
|
+ return parseInt(evalMatch[1], 10);
|
|
|
+ }
|
|
|
+
|
|
|
+ var userCodeMatch = line.match(/<user-code>:(\d+):/);
|
|
|
+ if (userCodeMatch) {
|
|
|
+ return parseInt(userCodeMatch[1], 10);
|
|
|
+ }
|
|
|
+
|
|
|
+ var anonymousMatch = line.match(/<anonymous>:(\d+):/);
|
|
|
+ if (anonymousMatch) {
|
|
|
+ return parseInt(anonymousMatch[1], 10);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+}
|
|
|
+
|
|
|
+async function execute(config, input, context) {
|
|
|
+ var code = config.code;
|
|
|
+ var timeout = config.timeout || 30;
|
|
|
+
|
|
|
+ if (!code || typeof code !== 'string') {
|
|
|
+ throw new Error('No code provided');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (timeout < 1) timeout = 1;
|
|
|
+ if (timeout > 300) timeout = 300;
|
|
|
+
|
|
|
+ var helpers = createHelpers();
|
|
|
+
|
|
|
+ var startTime = Date.now();
|
|
|
+
|
|
|
+ smartbotic.log.info('Executing custom code (' + code.length + ' chars, timeout: ' + timeout + 's)');
|
|
|
+
|
|
|
+ try {
|
|
|
+ var userInput = input;
|
|
|
+ var userContext = context;
|
|
|
+ var _ = helpers;
|
|
|
+
|
|
|
+ var userCodeFunction = new Function('input', 'context', '_', '"use strict";\n' + code);
|
|
|
+
|
|
|
+ var result = userCodeFunction(userInput, userContext, _);
|
|
|
+
|
|
|
+ var executionTime = Date.now() - startTime;
|
|
|
+
|
|
|
+ smartbotic.log.info('Code executed successfully in ' + executionTime + 'ms');
|
|
|
+
|
|
|
+ return {
|
|
|
+ result: result,
|
|
|
+ executionTime: executionTime
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ var executionTime = Date.now() - startTime;
|
|
|
+ var lineNumber = extractLineNumber(error);
|
|
|
+ var errorMessage = error.message || String(error);
|
|
|
+
|
|
|
+ if (lineNumber !== null) {
|
|
|
+ errorMessage = 'Line ' + lineNumber + ': ' + errorMessage;
|
|
|
+ }
|
|
|
+
|
|
|
+ smartbotic.log.error('Code execution failed: ' + errorMessage);
|
|
|
+
|
|
|
+ throw new Error(errorMessage);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+module.exports = { configSchema, inputSchema, outputSchema, execute };
|