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:
@@ -21,11 +21,16 @@ import {
|
||||
installPluginFromZipBuffer,
|
||||
} from "../../plugin-install.js";
|
||||
import { createDryRunLogger, safeSerialize } from "./dry-run-logger.js";
|
||||
import {
|
||||
encodeBinaryForWire,
|
||||
reviveBinaryFromWire,
|
||||
} from "../../json-preview.js";
|
||||
import { normalizeStepResult } from "../../step-result.js";
|
||||
import { resolveConfigRefs } from "../../config-refs.js";
|
||||
import { getAppVersion } from "../../app-version.js";
|
||||
import { EXAMPLE_PLUGINS_DIR } from "../../paths.js";
|
||||
import { pluginScriptRef } from "../../plugin-manifest.js";
|
||||
import { evaluateJsonata, SET_STEP_SCRIPT } from "../../workflow-parse.js";
|
||||
|
||||
/**
|
||||
* @param {{ referencedScripts: () => Set<string> }} registry
|
||||
@@ -249,12 +254,9 @@ export default function scriptsPluginFactory(registry) {
|
||||
const rawName = decodeURIComponent(
|
||||
/** @type {{ name: string }} */ (req.params).name,
|
||||
);
|
||||
const body = /** @type {{ content?: string, data?: unknown, context?: unknown, config?: unknown, owner?: string }} */ (
|
||||
const body = /** @type {{ content?: string, expression?: string, data?: unknown, context?: unknown, config?: unknown, owner?: string }} */ (
|
||||
req.body ?? {}
|
||||
);
|
||||
if (typeof body.content !== "string") {
|
||||
return reply.code(400).send({ error: "content is required" });
|
||||
}
|
||||
|
||||
let owner = "default";
|
||||
if (body.owner != null && body.owner !== "") {
|
||||
@@ -265,12 +267,92 @@ export default function scriptsPluginFactory(registry) {
|
||||
}
|
||||
}
|
||||
|
||||
const incomingContext =
|
||||
const incomingContext = reviveBinaryFromWire(
|
||||
body.context != null &&
|
||||
typeof body.context === "object" &&
|
||||
!Array.isArray(body.context)
|
||||
typeof body.context === "object" &&
|
||||
!Array.isArray(body.context)
|
||||
? body.context
|
||||
: {};
|
||||
: {},
|
||||
);
|
||||
const incomingData = reviveBinaryFromWire(body.data ?? null);
|
||||
|
||||
const { log, logs } = createDryRunLogger();
|
||||
const started = Date.now();
|
||||
|
||||
// Set steps are inline JSONata (no script file). Match registry runCompiledStep.
|
||||
if (rawName === SET_STEP_SCRIPT || rawName === `${SET_STEP_SCRIPT}.js`) {
|
||||
const configObj =
|
||||
body.config != null &&
|
||||
typeof body.config === "object" &&
|
||||
!Array.isArray(body.config)
|
||||
? /** @type {Record<string, unknown>} */ (body.config)
|
||||
: null;
|
||||
const expression =
|
||||
typeof body.expression === "string"
|
||||
? body.expression
|
||||
: typeof configObj?.expression === "string"
|
||||
? configObj.expression
|
||||
: null;
|
||||
if (expression == null || !expression.trim()) {
|
||||
return reply.code(400).send({ error: "expression is required" });
|
||||
}
|
||||
|
||||
try {
|
||||
const config = await resolveConfigRefs(
|
||||
{ ...(configObj ?? {}), expression },
|
||||
{
|
||||
owner,
|
||||
workflowKey: "dry-run",
|
||||
context: incomingContext,
|
||||
},
|
||||
);
|
||||
const ctx = {
|
||||
data: incomingData,
|
||||
context: incomingContext,
|
||||
config,
|
||||
};
|
||||
const value = await evaluateJsonata(expression, ctx);
|
||||
const result = normalizeStepResult(
|
||||
{
|
||||
output: value,
|
||||
context: incomingContext,
|
||||
skipRemaining: false,
|
||||
},
|
||||
incomingContext,
|
||||
SET_STEP_SCRIPT,
|
||||
);
|
||||
log.info({ expression }, "set: dry-run evaluated");
|
||||
return {
|
||||
status: "success",
|
||||
output: safeSerialize(result.output),
|
||||
context: safeSerialize(result.context),
|
||||
wireOutput: encodeBinaryForWire(result.output),
|
||||
wireContext: encodeBinaryForWire(result.context),
|
||||
skipRemaining: result.skipRemaining,
|
||||
error: null,
|
||||
logs,
|
||||
durationMs: Date.now() - started,
|
||||
meta: null,
|
||||
metaError: null,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
status: "failed",
|
||||
output: null,
|
||||
context: null,
|
||||
skipRemaining: false,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
logs,
|
||||
durationMs: Date.now() - started,
|
||||
meta: null,
|
||||
metaError: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof body.content !== "string") {
|
||||
return reply.code(400).send({ error: "content is required" });
|
||||
}
|
||||
|
||||
const resolved = resolveScriptRef(rawName);
|
||||
const pluginDir =
|
||||
@@ -278,9 +360,6 @@ export default function scriptsPluginFactory(registry) {
|
||||
? resolved.pluginDir ?? null
|
||||
: null;
|
||||
|
||||
const { log, logs } = createDryRunLogger();
|
||||
const started = Date.now();
|
||||
|
||||
try {
|
||||
const config = await resolveConfigRefs(body.config ?? null, {
|
||||
owner,
|
||||
@@ -288,7 +367,7 @@ export default function scriptsPluginFactory(registry) {
|
||||
context: incomingContext,
|
||||
});
|
||||
const ctx = {
|
||||
data: body.data ?? null,
|
||||
data: incomingData,
|
||||
context: incomingContext,
|
||||
config,
|
||||
};
|
||||
@@ -312,6 +391,8 @@ export default function scriptsPluginFactory(registry) {
|
||||
status: "success",
|
||||
output: safeSerialize(result.output),
|
||||
context: safeSerialize(result.context),
|
||||
wireOutput: encodeBinaryForWire(result.output),
|
||||
wireContext: encodeBinaryForWire(result.context),
|
||||
skipRemaining: result.skipRemaining,
|
||||
error: null,
|
||||
logs,
|
||||
|
||||
Reference in New Issue
Block a user