forked from robinschalkowsky/evaluation
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexecute_eval_program.mjs
More file actions
107 lines (93 loc) · 3.03 KB
/
Copy pathexecute_eval_program.mjs
File metadata and controls
107 lines (93 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import fs from "node:fs/promises";
import vm from "node:vm";
import jsonata from "jsonata";
const VM_TIMEOUT_MS = 10_000;
function readArg(name) {
const index = process.argv.indexOf(name);
if (index === -1 || index + 1 >= process.argv.length) {
throw new Error(`Missing required argument: ${name}`);
}
return process.argv[index + 1];
}
async function loadJson(path) {
const raw = await fs.readFile(path, "utf8");
return JSON.parse(raw);
}
async function loadText(path) {
return await fs.readFile(path, "utf8");
}
async function runJavascript(programText, inputJson) {
const context = vm.createContext({
console,
__j2j_input: inputJson,
});
const wrappedProgram = `${programText}
globalThis.__j2j_candidate =
typeof mapRawToTarget === "function"
? mapRawToTarget
: (typeof transform === "function"
? transform
: (typeof module !== "undefined" && typeof module.exports === "function"
? module.exports
: null));
`;
const script = new vm.Script(wrappedProgram, {filename: "generated_mapping.js"});
script.runInContext(context, {timeout: VM_TIMEOUT_MS});
const candidate = context.__j2j_candidate;
if (typeof candidate !== "function") {
throw new Error(
"Generated JavaScript did not define a callable transform(input) or mapRawToTarget(input) function.",
);
}
const invocation = new vm.Script(
"Promise.resolve(globalThis.__j2j_candidate(globalThis.__j2j_input))",
{filename: "generated_mapping_invocation.js"},
);
const result = await invocation.runInContext(context, {timeout: VM_TIMEOUT_MS});
if (typeof result === "undefined") {
throw new Error("Generated JavaScript returned undefined.");
}
return result;
}
async function runJsonata(programText, inputJson) {
let expression;
try {
expression = jsonata(programText);
} catch (error) {
throw new Error(`JSONata compile error: ${formatJsonataError(error)}`);
}
try {
return await expression.evaluate(inputJson);
} catch (error) {
throw new Error(`JSONata runtime error: ${formatJsonataError(error)}`);
}
}
function formatJsonataError(error) {
const details = [];
for (const key of ["code", "position", "token", "value", "message"]) {
if (error?.[key] !== undefined) {
details.push(`${key}=${JSON.stringify(error[key])}`);
}
}
return details.length > 0 ? details.join(", ") : String(error);
}
async function main() {
const mode = readArg("--mode");
const programFile = readArg("--program-file");
const inputFile = readArg("--input-file");
const programText = await loadText(programFile);
const inputJson = await loadJson(inputFile);
let result;
if (mode === "javascript") {
result = await runJavascript(programText, inputJson);
} else if (mode === "jsonata") {
result = await runJsonata(programText, inputJson);
} else {
throw new Error(`Unsupported mode: ${mode}`);
}
process.stdout.write(`${JSON.stringify(result)}\n`);
}
main().catch(error => {
process.stderr.write(`${error?.stack || String(error)}\n`);
process.exit(1);
});