@nocobase/flow-engine
Version:
A standalone flow engine for NocoBase, managing workflows, models, and actions.
265 lines (263 loc) • 11.4 kB
JavaScript
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var FlowErrorFallback_exports = {};
__export(FlowErrorFallback_exports, {
FlowErrorFallback: () => FlowErrorFallback,
FlowErrorFallbackModal: () => FlowErrorFallbackModal
});
module.exports = __toCommonJS(FlowErrorFallback_exports);
var import_antd = require("antd");
var import_react = __toESM(require("react"));
var import_useFlowModel = require("../hooks/useFlowModel");
var import_utils = require("../utils");
const { Paragraph, Text } = import_antd.Typography;
const FlowErrorFallbackInner = /* @__PURE__ */ __name(({ error, resetErrorBoundary }) => {
var _a;
const [loading, setLoading] = (0, import_react.useState)(false);
const model = (0, import_useFlowModel.useFlowModel)();
const t = (0, import_utils.getT)(model);
const handleCopyError = /* @__PURE__ */ __name(async () => {
setLoading(true);
try {
const errorInfo = {
message: error.message,
stack: error.stack,
modelInfo: model ? {
uid: model.uid,
className: model.constructor.name,
props: model.props,
stepParams: model.stepParams
} : null,
timestamp: (/* @__PURE__ */ new Date()).toISOString()
};
await navigator.clipboard.writeText(JSON.stringify(errorInfo, null, 2));
console.log("Error information copied to clipboard");
} catch (err) {
console.error("Failed to copy error information:", err);
}
setLoading(false);
}, "handleCopyError");
const canDownloadLogs = (_a = model == null ? void 0 : model.context) == null ? void 0 : _a.api;
const handleDownloadLogs = /* @__PURE__ */ __name(async () => {
if (!canDownloadLogs) {
console.error("API client not available");
return;
}
setLoading(true);
try {
const apiClient = model.context.api;
const location = {
pathname: window.location.pathname,
search: window.location.search,
hash: window.location.hash,
href: window.location.href
};
const res = await apiClient.request({
url: "logger:collect",
method: "post",
responseType: "blob",
data: {
error: {
message: error.message,
stack: error.stack
},
location,
modelInfo: model ? {
uid: model.uid,
className: model.constructor.name,
props: model.props,
stepParams: model.stepParams
} : null
}
});
const url = window.URL.createObjectURL(new Blob([res.data], { type: "application/gzip" }));
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", "logs.tar.gz");
link.click();
link.remove();
window.URL.revokeObjectURL(url);
} catch (err) {
console.error("Failed to download logs:", err);
}
setLoading(false);
}, "handleDownloadLogs");
const subTitle = /* @__PURE__ */ import_react.default.createElement("span", null, t("This is likely a NocoBase internals bug. Please open an issue at"), " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "https://github.com/nocobase/nocobase/issues", target: "_blank", rel: "noopener noreferrer" }, t("here")), model && /* @__PURE__ */ import_react.default.createElement("div", { style: { marginTop: "8px", fontSize: "12px", color: "#999" } }, "Model: ", model.constructor.name, " (uid: ", model.uid, ")"));
return /* @__PURE__ */ import_react.default.createElement("div", { style: { backgroundColor: "white" } }, /* @__PURE__ */ import_react.default.createElement(
import_antd.Result,
{
style: { maxWidth: "60vw", margin: "auto" },
status: "error",
title: t("Render failed"),
subTitle,
extra: [
/* @__PURE__ */ import_react.default.createElement(import_antd.Button, { type: "primary", key: "feedback", href: "https://github.com/nocobase/nocobase/issues", target: "_blank" }, t("Feedback")),
canDownloadLogs && /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { key: "log", loading, onClick: handleDownloadLogs }, t("Download logs")),
/* @__PURE__ */ import_react.default.createElement(import_antd.Button, { key: "copy", loading, onClick: handleCopyError }, t("Copy error info")),
resetErrorBoundary && /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { key: "retry", danger: true, onClick: resetErrorBoundary }, t("Try again"))
].filter(Boolean)
},
/* @__PURE__ */ import_react.default.createElement(Paragraph, { copyable: { text: error.stack } }, /* @__PURE__ */ import_react.default.createElement(Text, { type: "danger", style: { whiteSpace: "pre-line", textAlign: "center" } }, error.stack))
));
}, "FlowErrorFallbackInner");
const FlowErrorFallback = /* @__PURE__ */ __name(({ error, resetErrorBoundary }) => {
const [loading, setLoading] = (0, import_react.useState)(false);
try {
return /* @__PURE__ */ import_react.default.createElement(FlowErrorFallbackInner, { error, resetErrorBoundary });
} catch {
const handleCopyError = /* @__PURE__ */ __name(async () => {
setLoading(true);
try {
const errorInfo = {
message: error.message,
stack: error.stack,
timestamp: (/* @__PURE__ */ new Date()).toISOString()
};
await navigator.clipboard.writeText(JSON.stringify(errorInfo, null, 2));
console.log("Error information copied to clipboard");
} catch (err) {
console.error("Failed to copy error information:", err);
}
setLoading(false);
}, "handleCopyError");
const subTitle = /* @__PURE__ */ import_react.default.createElement("span", null, "This is likely a NocoBase internals bug. Please open an issue at ", /* @__PURE__ */ import_react.default.createElement("a", { href: "https://github.com/nocobase/nocobase/issues", target: "_blank", rel: "noopener noreferrer" }, "here"));
return /* @__PURE__ */ import_react.default.createElement("div", { style: { backgroundColor: "white" } }, /* @__PURE__ */ import_react.default.createElement(
import_antd.Result,
{
style: { maxWidth: "60vw", margin: "auto" },
status: "error",
title: "Render failed",
subTitle,
extra: [
/* @__PURE__ */ import_react.default.createElement(import_antd.Button, { type: "primary", key: "feedback", href: "https://github.com/nocobase/nocobase/issues", target: "_blank" }, "Feedback"),
/* @__PURE__ */ import_react.default.createElement(import_antd.Button, { key: "copy", loading, onClick: handleCopyError }, "Copy error info"),
resetErrorBoundary && /* @__PURE__ */ import_react.default.createElement(import_antd.Button, { key: "retry", danger: true, onClick: resetErrorBoundary }, "Try again")
].filter(Boolean)
},
/* @__PURE__ */ import_react.default.createElement(Paragraph, { copyable: { text: error.stack } }, /* @__PURE__ */ import_react.default.createElement(Text, { type: "danger", style: { whiteSpace: "pre-line", textAlign: "center" } }, error.stack))
));
}
}, "FlowErrorFallback");
const FlowErrorFallbackModal = /* @__PURE__ */ __name(({
error,
resetErrorBoundary,
children
}) => {
const [open, setOpen] = (0, import_react.useState)(false);
const defaultChildren = /* @__PURE__ */ import_react.default.createElement(
Paragraph,
{
style: {
display: "flex",
marginBottom: 0
},
copyable: { text: error.message }
},
/* @__PURE__ */ import_react.default.createElement(
Text,
{
type: "danger",
style: {
whiteSpace: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
display: "inline-block",
maxWidth: "200px"
}
},
"Error: ",
error.message
)
);
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { onMouseOver: () => setOpen(true) }, children || defaultChildren), open && /* @__PURE__ */ import_react.default.createElement(
"div",
{
style: {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
display: "flex",
alignItems: "center",
justifyContent: "center",
zIndex: 1e4
},
onClick: () => setOpen(false)
},
/* @__PURE__ */ import_react.default.createElement(
"div",
{
style: {
backgroundColor: "#fff",
borderRadius: "8px",
padding: "24px",
maxWidth: "60vw",
maxHeight: "80vh",
overflow: "auto",
position: "relative"
},
onClick: (e) => e.stopPropagation()
},
/* @__PURE__ */ import_react.default.createElement(
"button",
{
style: {
position: "absolute",
top: "8px",
right: "12px",
background: "none",
border: "none",
fontSize: "18px",
cursor: "pointer",
color: "#999"
},
onClick: () => setOpen(false)
},
"\xD7"
),
/* @__PURE__ */ import_react.default.createElement(FlowErrorFallback, { error, resetErrorBoundary })
)
));
}, "FlowErrorFallbackModal");
FlowErrorFallback.Modal = FlowErrorFallbackModal;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FlowErrorFallback,
FlowErrorFallbackModal
});