Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions typescript/src/ts/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function isValidFunctionName(name: unknown): name is string {
*/
export function createModuleTextFromProgram(jsonObject: object): Result<string> {
const steps = (jsonObject as Program)["@steps"];
if (!(Array.isArray(steps) && steps.every(step => typeof step === "object" && step !== null && step.hasOwnProperty("@func")))) {
if (!(Array.isArray(steps) && steps.every(step => typeof step === "object" && step !== null && Object.prototype.hasOwnProperty.call(step, "@func")))) {
return error("JSON object is not a valid program");
}
let hasError = false;
Expand All @@ -119,15 +119,15 @@ export function createModuleTextFromProgram(jsonObject: object): Result<string>
}

function objectToString(obj: Record<string, unknown>) {
if (obj.hasOwnProperty("@ref")) {
if (Object.prototype.hasOwnProperty.call(obj, "@ref")) {
const index = obj["@ref"];
if (typeof index === "number" && Number.isInteger(index) && index >= 0 && index < currentStep && Object.keys(obj).length === 1) {
return `step${index + 1}`;
}
}
else if (obj.hasOwnProperty("@func")) {
else if (Object.prototype.hasOwnProperty.call(obj, "@func")) {
const func = obj["@func"];
const hasArgs = obj.hasOwnProperty("@args");
const hasArgs = Object.prototype.hasOwnProperty.call(obj, "@args");
const args = hasArgs ? obj["@args"] : [];
if (isValidFunctionName(func) && (Array.isArray(args)) && Object.keys(obj).length === (hasArgs ? 2 : 1)) {
return `api.${func}(${arrayToString(args)})`;
Expand Down Expand Up @@ -171,16 +171,16 @@ export async function evaluateJsonProgram(program: Program, onCall: (func: strin
}

async function evaluateObject(obj: Record<string, unknown>) {
if (obj.hasOwnProperty("@ref")) {
if (Object.prototype.hasOwnProperty.call(obj, "@ref")) {
const index = obj["@ref"];
if (Object.keys(obj).length === 1 && typeof index === "number" && Number.isInteger(index) && index >= 0 && index < results.length) {
return results[index];
}
throw new Error(`Invalid result reference: ${String(index)}`);
}
else if (obj.hasOwnProperty("@func")) {
else if (Object.prototype.hasOwnProperty.call(obj, "@func")) {
const func = obj["@func"];
const hasArgs = obj.hasOwnProperty("@args");
const hasArgs = Object.prototype.hasOwnProperty.call(obj, "@args");
const args = hasArgs ? obj["@args"] : [];
if (!isValidFunctionName(func)) {
throw new Error(`Invalid function name: ${JSON.stringify(func)}`);
Expand Down
33 changes: 33 additions & 0 deletions typescript/test/program.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@ import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { evaluateJsonProgram, createModuleTextFromProgram, Program, FunctionCall } from "../dist/ts/index.js";

describe("program objects with own property names", () => {
const programs: Array<[string, Program]> = [
["a literal hasOwnProperty field", {
"@steps": [{ "@func": "identity", "@args": [{ hasOwnProperty: "user data", value: 7 }] }],
}],
["a null-prototype call and argument", {
"@steps": [Object.assign(Object.create(null), {
"@func": "identity",
"@args": [Object.assign(Object.create(null), { hasOwnProperty: "user data", value: 7 })],
})],
}],
];

for (const [name, program] of programs) {
it(`generates a module for ${name}`, () => {
const result = createModuleTextFromProgram(program);
assert.ok(result.success);
assert.equal(result.data,
'import { API } from "./schema";\nfunction program(api: API) {\n' +
' return api.identity({ "hasOwnProperty": "user data", "value": 7 });\n}');
});

it(`evaluates ${name} without losing the argument`, async () => {
const result = await evaluateJsonProgram(program, async (func, args) => {
assert.equal(func, "identity");
assert.deepEqual(args, [{ hasOwnProperty: "user data", value: 7 }]);
return args[0];
});
assert.deepEqual(result, { hasOwnProperty: "user data", value: 7 });
});
}
});

// ---------------------------------------------------------------------------
// evaluateJsonProgram result-reference bounds checking
// ---------------------------------------------------------------------------
Expand Down