feat(server): enhance workflow and script handling with new features

- Added `workflowLastModifiedAt` function to retrieve the last modified timestamp of workflow YAML files.
- Introduced `encodeBinaryForWire` and `reviveBinaryFromWire` functions for better handling of binary data in JSON.
- Implemented `useDeleteKv` hook for deleting key-value pairs in the web API.
- Enhanced `scriptsPluginFactory` to support dry-run evaluations with JSONata expressions.
- Added `set-dry-run-smoke.js` test to validate dry-run functionality.
- Updated profile configuration to include `overlayFromMerged` for better profile management.
- Introduced try-session management in the web components to handle step execution states.
- Improved various components to support new try-session features and maintain UI consistency.

Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
This commit is contained in:
2026-08-22 17:52:17 +07:00
parent 72feb39d56
commit beb7e22652
28 changed files with 1753 additions and 143 deletions
+20 -1
View File
@@ -1,4 +1,9 @@
import { jsonPreviewReplacer, summarizeBinary } from "../json-preview.js";
import {
encodeBinaryForWire,
jsonPreviewReplacer,
reviveBinaryFromWire,
summarizeBinary,
} from "../json-preview.js";
import { serialize, toDisplayValue } from "../store.js";
import { safeSerialize } from "../src/api/dry-run-logger.js";
@@ -53,4 +58,18 @@ if (typed.file.length !== png.length || typed.file.type !== "Buffer") {
throw new Error(`Uint8Array: ${JSON.stringify(typed)}`);
}
const wired = encodeBinaryForWire({ file: png, n: 1n });
if (wired.n !== "1" || wired.file.encoding !== "base64" || typeof wired.file.data !== "string") {
throw new Error(`encodeBinaryForWire: ${JSON.stringify(wired)}`);
}
const revived = reviveBinaryFromWire(JSON.parse(JSON.stringify(wired)));
if (!Buffer.isBuffer(revived.file) || !revived.file.equals(png)) {
throw new Error("reviveBinaryFromWire failed to restore bytes");
}
const previewOnly = { type: "Buffer", length: png.length, preview: "89", truncated: true };
const left = reviveBinaryFromWire(previewOnly);
if (Buffer.isBuffer(left)) {
throw new Error("preview-only summary should not revive");
}
console.log("json-preview-smoke: ok");
+81
View File
@@ -0,0 +1,81 @@
/**
* Smoke: set dry-run path (evaluateJsonata + envelope), mirrors
* POST /scripts/set/dry-run in src/api/scripts.js.
*/
import assert from "node:assert/strict";
import { evaluateJsonata, SET_STEP_SCRIPT } from "../workflow-parse.js";
import { normalizeStepResult } from "../step-result.js";
import { safeSerialize } from "../src/api/dry-run-logger.js";
assert.equal(SET_STEP_SCRIPT, "set");
async function dryRunSet({ expression, data, context = {} }) {
if (typeof expression !== "string" || !expression.trim()) {
throw new Error("expression is required");
}
const incomingContext =
context != null && typeof context === "object" && !Array.isArray(context)
? context
: {};
const config = { expression };
const ctx = { data: data ?? null, context: incomingContext, config };
const value = await evaluateJsonata(expression, ctx);
const result = normalizeStepResult(
{ output: value, context: incomingContext, skipRemaining: false },
incomingContext,
SET_STEP_SCRIPT,
);
return {
status: "success",
output: safeSerialize(result.output),
context: safeSerialize(result.context),
skipRemaining: result.skipRemaining,
};
}
{
const res = await dryRunSet({
expression: '{"title": data.title, "ok": true}',
data: { title: "Hello" },
context: { runId: "dry" },
});
assert.equal(res.status, "success");
assert.deepEqual(res.output, { title: "Hello", ok: true });
assert.deepEqual(res.context, { runId: "dry" });
assert.equal(res.skipRemaining, false);
}
{
const res = await dryRunSet({
expression: "data.count + 1",
data: { count: 41 },
context: { token: "abc" },
});
assert.equal(res.output, 42);
// Sets never mutate context
assert.deepEqual(res.context, { token: "abc" });
}
{
let hit = false;
try {
await dryRunSet({ expression: " ", data: {} });
} catch (err) {
hit = true;
assert.match(String(err.message), /expression is required/);
}
assert.equal(hit, true);
}
{
let hit = false;
try {
await dryRunSet({ expression: "data.{" , data: {} });
} catch (err) {
hit = true;
assert.ok(err instanceof Error);
}
assert.equal(hit, true);
}
console.log("set-dry-run-smoke: ok");