@comake/skl-js-engine
Version:
Standard Knowledge Language Javascript Engine
217 lines (188 loc) • 6.68 kB
JavaScript
// executor.ts - Updated for current Deno versions
// Set up process communication
const decoder = new TextDecoder();
const encoder = new TextEncoder();
// Function to safely stringify any value
function safeStringify(obj, indent = 2) {
const cache = new Set();
return JSON.stringify(
obj,
(key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.has(value)) {
return '[Circular Reference]';
}
cache.add(value);
}
// Handle special types that don't stringify well
if (value instanceof Error) {
return { name: value.name, message: value.message, stack: value.stack };
}
if (value instanceof RegExp || value instanceof Date) {
return value.toString();
}
if (typeof value === 'function') {
return `[Function: ${value.name || 'anonymous'}]`;
}
return value;
},
indent,
);
}
// Import available plugins
// import { toArrayBuffer } from 'https://deno.land/std/streams/mod.ts';
import { Buffer } from 'node:buffer';
import { arrayBuffer } from 'node:stream/consumers';
// Define utility functions
const utils = {
formatDate: (date, format = 'ISO') => {
const dateObj = date instanceof Date ? date : new Date(date);
if (format === 'ISO') return dateObj.toISOString();
if (format === 'UTC') return dateObj.toUTCString();
return dateObj.toString();
},
slugify: (text) => {
return text
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-');
},
groupBy: (array, key) => {
return array.reduce((result, item) => {
const groupKey = item[key];
if (!result[groupKey]) {
result[groupKey] = [];
}
result[groupKey].push(item);
return result;
}, {});
},
};
// Check if code contains a function definition
function containsNamedFunction(code, functionName) {
// Look for function declarations
const functionRegex = new RegExp(`function\\s+${functionName}\\s*\\(`, 'g');
if (functionRegex.test(code)) return true;
// Look for arrow functions or function expressions
const arrowFunctionRegex = new RegExp(`(const|let|var)\\s+${functionName}\\s*=\\s*(async\\s+)?\\(?.*\\)?\\s*=>`, 'g');
if (arrowFunctionRegex.test(code)) return true;
// Look for object method definitions
const methodRegex = new RegExp(`${functionName}\\s*\\(.*\\)\\s*{`, 'g');
if (methodRegex.test(code)) return true;
return false;
}
// Main execution function
async function executeCode() {
try {
// Read input from stdin - updated for current Deno versions
const logs = [];
const inputText = decoder.decode(await arrayBuffer(Deno.stdin.readable));
const decodedText = Buffer.from(inputText, 'base64');
const decodedData = decoder.decode(decodedText);
// Deno.writeTextFileSync('/Users/avneeshraghav/Downloads/deno.txt', decodedData);
const inputData = JSON.parse(decodedData);
// Extract execution details
const { code, args, functionName = 'main', skdsEndpointUrl } = inputData;
const sklEngine = new SKLEngine({
type: "sparql",
endpointUrl: skdsEndpointUrl,
});
// Setup execution environment
// Custom console implementation
const console = {
log: (...args) => {
logs.push(args.map((arg) => (typeof arg === 'object' ? safeStringify(arg) : String(arg))).join(' '));
},
error: (...args) => {
logs.push(
`ERROR: ${args.map((arg) => (typeof arg === 'object' ? safeStringify(arg) : String(arg))).join(' ')}`,
);
},
warn: (...args) => {
logs.push(`WARN: ${args.map((arg) => (typeof arg === 'object' ? safeStringify(arg) : String(arg))).join(' ')}`);
},
info: (...args) => {
logs.push(`INFO: ${args.map((arg) => (typeof arg === 'object' ? safeStringify(arg) : String(arg))).join(' ')}`);
},
};
// Result object
const result = {
logs,
error: null,
result: null,
executionTime: 0,
};
try {
const startTime = performance.now();
// Wrap code execution in an async function to allow top-level await
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
// Determine how to handle the code
let finalCode = code;
const hasNamedFunction = containsNamedFunction(code, functionName);
// If no main function is defined, wrap the code
if (!hasNamedFunction) {
// Check if the code is a simple expression (no statements ending with ;)
const isSimpleExpression =
!code.includes(';') &&
!code.includes('function') &&
!code.includes('class') &&
!code.includes('if') &&
!code.includes('for') &&
!code.trim().startsWith('//');
if (isSimpleExpression) {
// For simple expressions, return the evaluated result directly
finalCode = `function ${functionName}() { return (${code}); }`;
} else {
// For code blocks, wrap everything in a function
finalCode = `function ${functionName}() {\n${code}\n}`;
}
}
// Create the execution function with the processed code
const executeFn = new AsyncFunction(
'args',
'console',
'utils',
'sklEngine',
`
${finalCode}
// Return result from the specified function
if (typeof ${functionName} === 'function') {
return ${functionName}();
} else {
throw new Error("Function '${functionName}' is not defined");
}
`,
);
// Execute with the provided context
result.result = await executeFn(args, console, utils, sklEngine);
result.executionTime = performance.now() - startTime;
} catch (error) {
result.error = {
message: error.message || 'Unknown execution error',
name: error.name,
stack: error.stack,
};
}
// Write result to stdout - updated for current Deno versions
await Deno.stdout.write(encoder.encode(safeStringify(result)));
} catch (err) {
// Handle errors in the outer execution context
const errorResult = {
logs: [],
error: {
message: `Error in Deno execution context: ${err.message}`,
name: err.name,
stack: err.stack,
},
result: null,
executionTime: 0,
};
await Deno.stdout.write(encoder.encode(JSON.stringify(errorResult)));
}
}
// Execute and catch any top-level errors
executeCode().catch((err) => {
console.error('Fatal error:', err);
Deno.exit(1);
});