@burdenoff/fluidgrids-fe-sdk 2026.1028.7 → 2026.1028.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ExecutionDetailPage-6T7XEVUA.js +20 -0
- package/dist/{ExecutionsListPage-BT3ADBXP.js → ExecutionsListPage-FABXLXWT.js} +7 -7
- package/dist/WorkflowBuilder-J5APOMKG.js +24 -0
- package/dist/{WorkflowsList-FZ4W64YZ.js → WorkflowsList-4HG3KBMR.js} +9 -7
- package/dist/{chunk-JNJDVC7V.js → chunk-2ZV6UJD6.js} +2 -2
- package/dist/{chunk-5YP3U7BU.js → chunk-3GOZYLTM.js} +3 -3
- package/dist/{chunk-NW6H7EN3.js → chunk-3ZBS2R6F.js} +42 -6
- package/dist/{chunk-QMBXEECD.js → chunk-4NRANBK7.js} +7 -7
- package/dist/{chunk-MKKBEVXO.js → chunk-6T5EBO7G.js} +1 -1
- package/dist/chunk-77NA7TPJ.js +38 -0
- package/dist/{chunk-QW24ZBSR.js → chunk-7OJDILWM.js} +1 -1
- package/dist/{chunk-6J4RR46J.js → chunk-7PBOU7JC.js} +2 -1
- package/dist/{chunk-U7WLROQT.js → chunk-B7UN6FGD.js} +3 -3
- package/dist/{chunk-LOZ6XNOX.js → chunk-DOPSPCO3.js} +26 -1
- package/dist/{chunk-RJ3YK6O7.js → chunk-EAF47LYM.js} +368 -103
- package/dist/{chunk-PBX42F4Q.js → chunk-GGINSR75.js} +6 -5
- package/dist/{chunk-DUXOFCPO.js → chunk-GQQV2L74.js} +1 -1
- package/dist/{chunk-ZB4BNTWR.js → chunk-KFCCLXZO.js} +4 -4
- package/dist/chunk-M62CJVTR.js +458 -0
- package/dist/{chunk-6NUHJPGJ.js → chunk-NVUJFHTQ.js} +1 -1
- package/dist/{chunk-27XYEFCK.js → chunk-OAFNNQN7.js} +1 -1
- package/dist/{chunk-IRKB2GGJ.js → chunk-OYFBLD5F.js} +3 -3
- package/dist/{chunk-XYZKIFXY.js → chunk-PXIWI7OB.js} +464 -31
- package/dist/{chunk-MUOJVRW7.js → chunk-SQ6VSXFI.js} +2 -2
- package/dist/{chunk-3ENHRYM5.js → chunk-TX6WIT3N.js} +1 -1
- package/dist/{chunk-T7TRJBCG.js → chunk-VFPIMW52.js} +3 -14
- package/dist/{chunk-2YDUYSB3.js → chunk-XAOMAVHB.js} +4 -4
- package/dist/{chunk-RKMTSOYH.js → chunk-YNOR2AGJ.js} +2 -2
- package/dist/{chunk-PYCKEQ4W.js → chunk-YQZ7HBOJ.js} +5 -3
- package/dist/{chunk-BBORXXGZ.js → chunk-ZFCXCSY4.js} +1 -1
- package/dist/executions.js +12 -12
- package/dist/history.js +4 -4
- package/dist/index.d.ts +1292 -55
- package/dist/index.js +36 -35
- package/dist/nodes.js +2 -2
- package/dist/observability.js +7 -6
- package/dist/settings.js +7 -6
- package/dist/templates.js +11 -10
- package/dist/workflow-builder.js +13 -12
- package/dist/workflows-list.js +9 -7
- package/package.json +1 -1
- package/dist/ExecutionDetailPage-A4JQGL34.js +0 -20
- package/dist/WorkflowBuilder-SQQI4VSF.js +0 -23
- package/dist/chunk-HDUCP6EO.js +0 -59
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { throwIfResultError, describeOperationError } from './chunk-
|
|
1
|
+
import { throwIfResultError, describeOperationError } from './chunk-NVUJFHTQ.js';
|
|
2
2
|
import { useTr } from './chunk-UULVIT25.js';
|
|
3
3
|
import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
|
|
4
4
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useImportWorkflowTemplate, FlowBuilderPage, useWorkflowTemplates, TemplateCard } from './chunk-
|
|
2
|
-
import { QuotaExhaustedDialog } from './chunk-
|
|
3
|
-
import { useWorkflowDefinitions, useDuplicateWorkflow } from './chunk-
|
|
1
|
+
import { useImportWorkflowTemplate, FlowBuilderPage, useWorkflowTemplates, TemplateCard } from './chunk-PXIWI7OB.js';
|
|
2
|
+
import { QuotaExhaustedDialog } from './chunk-M62CJVTR.js';
|
|
3
|
+
import { useWorkflowDefinitions, useDuplicateWorkflow } from './chunk-YQZ7HBOJ.js';
|
|
4
4
|
import { templatesControls } from './chunk-D3UWK7GY.js';
|
|
5
5
|
import { useFluidGridsPageControls } from './chunk-PJAWNFJV.js';
|
|
6
6
|
import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
|
|
7
|
-
import { BILLING_PLANS_PATH, useFluidGridsAnalytics, emitTemplateUsed, isQuotaExhaustedFailure } from './chunk-
|
|
7
|
+
import { BILLING_PLANS_PATH, useFluidGridsAnalytics, emitTemplateUsed, isQuotaExhaustedFailure } from './chunk-NVUJFHTQ.js';
|
|
8
8
|
import { useTr } from './chunk-UULVIT25.js';
|
|
9
9
|
import { useState, useEffect, useMemo, useCallback } from 'react';
|
|
10
10
|
import { useParams, useNavigate } from 'react-router';
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import { describeOperationError, useFluidGridsAnalytics, emitWorkflowLimitHit, emitUpgradeClicked, WORKFLOWS_QUOTA_NAME, throwIfResultError } from './chunk-NVUJFHTQ.js';
|
|
2
|
+
import { ExportWorkflowCodeDocument } from './chunk-7PBOU7JC.js';
|
|
3
|
+
import { useTr } from './chunk-UULVIT25.js';
|
|
4
|
+
import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
|
|
5
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
6
|
+
import JSZip from 'jszip';
|
|
7
|
+
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from '@burdenoff/fe-libs/ui';
|
|
8
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
// src/utils/workflow-transfer.ts
|
|
11
|
+
function asRecord(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
13
|
+
}
|
|
14
|
+
function collectWorkflowNodeReferences(definition) {
|
|
15
|
+
const root = asRecord(definition);
|
|
16
|
+
const data = asRecord(root?.data);
|
|
17
|
+
const nodes = data?.nodes;
|
|
18
|
+
if (!Array.isArray(nodes)) return [];
|
|
19
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20
|
+
const references = [];
|
|
21
|
+
for (const candidate of nodes) {
|
|
22
|
+
const node = asRecord(candidate);
|
|
23
|
+
const nodeData = asRecord(node?.data);
|
|
24
|
+
const nodeId = typeof node?.id === "string" ? node.id : null;
|
|
25
|
+
const nodeType = typeof nodeData?.nodeId === "string" ? nodeData.nodeId : node?.type;
|
|
26
|
+
if (!nodeId || typeof nodeType !== "string") continue;
|
|
27
|
+
const nodeVersion = typeof nodeData?.nodeVersion === "string" ? nodeData.nodeVersion : null;
|
|
28
|
+
const key = `${nodeId}:${nodeType}:${nodeVersion ?? ""}`;
|
|
29
|
+
if (seen.has(key)) continue;
|
|
30
|
+
seen.add(key);
|
|
31
|
+
references.push({ nodeId, nodeType, nodeVersion });
|
|
32
|
+
}
|
|
33
|
+
return references;
|
|
34
|
+
}
|
|
35
|
+
function createWorkflowExportPayload(workflow) {
|
|
36
|
+
const definitionJson = asRecord(workflow.definitionJson);
|
|
37
|
+
if (!definitionJson)
|
|
38
|
+
throw new Error("Workflow definition JSON must be an object");
|
|
39
|
+
return {
|
|
40
|
+
format: "fluidgrids.workflow",
|
|
41
|
+
schemaVersion: 1,
|
|
42
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
43
|
+
workflowKey: workflow.workflowKey,
|
|
44
|
+
version: workflow.version,
|
|
45
|
+
name: workflow.name,
|
|
46
|
+
description: workflow.description ?? null,
|
|
47
|
+
definitionJson,
|
|
48
|
+
context: asRecord(workflow.context),
|
|
49
|
+
tags: workflow.tags,
|
|
50
|
+
tagsV2: workflow.tagsV2,
|
|
51
|
+
icon: workflow.icon ?? null,
|
|
52
|
+
category: workflow.category ?? null,
|
|
53
|
+
nodeReferences: collectWorkflowNodeReferences(definitionJson)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function downloadBlob(filename, blob) {
|
|
57
|
+
const url = URL.createObjectURL(blob);
|
|
58
|
+
const anchor = document.createElement("a");
|
|
59
|
+
anchor.href = url;
|
|
60
|
+
anchor.download = filename;
|
|
61
|
+
anchor.click();
|
|
62
|
+
URL.revokeObjectURL(url);
|
|
63
|
+
}
|
|
64
|
+
function downloadWorkflowExport(workflow) {
|
|
65
|
+
const payload = createWorkflowExportPayload(workflow);
|
|
66
|
+
const blob = new Blob([JSON.stringify(payload, null, 2)], {
|
|
67
|
+
type: "application/json"
|
|
68
|
+
});
|
|
69
|
+
downloadBlob(
|
|
70
|
+
`${workflow.workflowKey}-${workflow.version}.fluidgrids.json`,
|
|
71
|
+
blob
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function parseWorkflowImport(raw) {
|
|
75
|
+
const parsed = JSON.parse(raw);
|
|
76
|
+
const root = asRecord(parsed);
|
|
77
|
+
if (!root) throw new Error("Import must contain a JSON object");
|
|
78
|
+
const definitionJson = asRecord(root.definitionJson) ?? root;
|
|
79
|
+
const name = typeof root.name === "string" && root.name.trim() ? root.name : "Imported workflow";
|
|
80
|
+
return {
|
|
81
|
+
definitionJson,
|
|
82
|
+
name,
|
|
83
|
+
workflowKey: typeof root.workflowKey === "string" ? root.workflowKey : void 0,
|
|
84
|
+
version: typeof root.version === "string" ? root.version : void 0,
|
|
85
|
+
description: typeof root.description === "string" ? root.description : void 0,
|
|
86
|
+
context: asRecord(root.context) ?? void 0,
|
|
87
|
+
tags: Array.isArray(root.tags) && root.tags.every((tag) => typeof tag === "string") ? root.tags : void 0,
|
|
88
|
+
tagsV2: root.tagsV2,
|
|
89
|
+
icon: typeof root.icon === "string" ? root.icon : void 0,
|
|
90
|
+
category: typeof root.category === "string" ? root.category : void 0,
|
|
91
|
+
nodeReferences: Array.isArray(root.nodeReferences) ? root.nodeReferences : collectWorkflowNodeReferences(definitionJson)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
var CODE_EXPORT_FORMATS = [
|
|
95
|
+
"TYPESCRIPT" /* Typescript */,
|
|
96
|
+
"PYTHON" /* Python */,
|
|
97
|
+
"JSON" /* Json */
|
|
98
|
+
];
|
|
99
|
+
var LOSSY_ITEM_KINDS = /* @__PURE__ */ new Set([
|
|
100
|
+
"layout-omitted",
|
|
101
|
+
"pinned-data-omitted",
|
|
102
|
+
"roundtrip-mismatch",
|
|
103
|
+
"lowering-failed"
|
|
104
|
+
]);
|
|
105
|
+
function isLossyFidelityItem(item) {
|
|
106
|
+
return LOSSY_ITEM_KINDS.has(item.kind);
|
|
107
|
+
}
|
|
108
|
+
function isLosslessReport(fidelity) {
|
|
109
|
+
return fidelity.level === "LOSSLESS" /* Lossless */;
|
|
110
|
+
}
|
|
111
|
+
function sortFidelityItems(items) {
|
|
112
|
+
return items.map((item, index) => ({ item, index })).sort((a, b) => {
|
|
113
|
+
const lossy = Number(isLossyFidelityItem(b.item)) - Number(isLossyFidelityItem(a.item));
|
|
114
|
+
if (lossy !== 0) return lossy;
|
|
115
|
+
return a.index - b.index;
|
|
116
|
+
}).map(({ item }) => item);
|
|
117
|
+
}
|
|
118
|
+
var EXTENSION_OF = {
|
|
119
|
+
["TYPESCRIPT" /* Typescript */]: ".ts",
|
|
120
|
+
["PYTHON" /* Python */]: ".py",
|
|
121
|
+
["JSON" /* Json */]: ".json"
|
|
122
|
+
};
|
|
123
|
+
function isGeneratedClient(path) {
|
|
124
|
+
return path.startsWith(".fluidgrids/");
|
|
125
|
+
}
|
|
126
|
+
function primaryExportFile(files, format) {
|
|
127
|
+
const extension = EXTENSION_OF[format];
|
|
128
|
+
const sidecar = /\.(layout|pinned)\.json$/;
|
|
129
|
+
return files.find(
|
|
130
|
+
(file) => !isGeneratedClient(file.path) && file.path.endsWith(extension) && !sidecar.test(file.path)
|
|
131
|
+
) ?? files[0] ?? null;
|
|
132
|
+
}
|
|
133
|
+
function orderedExportFiles(files, format) {
|
|
134
|
+
const primary = primaryExportFile(files, format);
|
|
135
|
+
if (!primary) return [];
|
|
136
|
+
return [primary, ...files.filter((file) => file !== primary)];
|
|
137
|
+
}
|
|
138
|
+
function fileBaseName(path) {
|
|
139
|
+
const parts = path.split("/").filter(Boolean);
|
|
140
|
+
return parts[parts.length - 1] ?? path;
|
|
141
|
+
}
|
|
142
|
+
function safeSegment(value) {
|
|
143
|
+
return value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
144
|
+
}
|
|
145
|
+
var ARCHIVE_SUFFIX = {
|
|
146
|
+
["TYPESCRIPT" /* Typescript */]: "typescript",
|
|
147
|
+
["PYTHON" /* Python */]: "python",
|
|
148
|
+
["JSON" /* Json */]: "json"
|
|
149
|
+
};
|
|
150
|
+
function exportArchiveName(workflowKey, version, format) {
|
|
151
|
+
const stem = [safeSegment(workflowKey), safeSegment(version)].filter(Boolean).join("-");
|
|
152
|
+
return `${stem || "workflow"}.${ARCHIVE_SUFFIX[format]}.zip`;
|
|
153
|
+
}
|
|
154
|
+
function exportFileContent(file) {
|
|
155
|
+
if (typeof file.content === "string") return file.content;
|
|
156
|
+
throw new Error(`The export of ${file.path} has no content`);
|
|
157
|
+
}
|
|
158
|
+
async function buildExportDownload(result, format) {
|
|
159
|
+
const files = orderedExportFiles(result.files, format);
|
|
160
|
+
if (files.length === 0) throw new Error("The export returned no files");
|
|
161
|
+
const contents = files.map((file) => ({
|
|
162
|
+
file,
|
|
163
|
+
content: exportFileContent(file)
|
|
164
|
+
}));
|
|
165
|
+
if (contents.length === 1) {
|
|
166
|
+
const [{ file, content }] = contents;
|
|
167
|
+
return {
|
|
168
|
+
filename: fileBaseName(file.path),
|
|
169
|
+
blob: new Blob([content], { type: file.contentType || "text/plain" })
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const zip = new JSZip();
|
|
173
|
+
for (const { file, content } of contents) zip.file(file.path, content);
|
|
174
|
+
return {
|
|
175
|
+
filename: exportArchiveName(result.workflowKey, result.version, format),
|
|
176
|
+
blob: await zip.generateAsync({
|
|
177
|
+
type: "blob",
|
|
178
|
+
mimeType: "application/zip"
|
|
179
|
+
})
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function importFileKind(name) {
|
|
183
|
+
const path = name.split(/[?#]/, 1)[0].toLowerCase();
|
|
184
|
+
if (path.endsWith(".json")) return "json";
|
|
185
|
+
if (path.endsWith(".ts") && !path.endsWith(".d.ts")) return "typescript";
|
|
186
|
+
if (path.endsWith(".zip")) return "archive";
|
|
187
|
+
if (path.endsWith(".py")) return "python";
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
function importAcceptAttribute(codeFirst) {
|
|
191
|
+
return codeFirst ? "application/json,.json,.ts,text/typescript,.zip,application/zip" : "application/json,.json";
|
|
192
|
+
}
|
|
193
|
+
var MAX_IMPORT_SOURCE_BYTES = 1024 * 1024;
|
|
194
|
+
var MAX_IMPORT_SOURCE_FILES = 200;
|
|
195
|
+
var MAX_IMPORT_ARCHIVE_BYTES = 2 * MAX_IMPORT_SOURCE_BYTES;
|
|
196
|
+
var MAX_ARCHIVE_ENTRIES = 1e3;
|
|
197
|
+
var NODE_CLIENTS_PATH = ".fluidgrids/nodes.ts";
|
|
198
|
+
var NODE_CLIENTS_IMPORT = /\bfrom\s*['"][./]*\.fluidgrids\/nodes(?:\.[cm]?[jt]s)?['"]/;
|
|
199
|
+
var SEGMENT = "[A-Za-z0-9_][A-Za-z0-9._-]*";
|
|
200
|
+
var PROJECT_SOURCE_PATH = new RegExp(
|
|
201
|
+
`^(?:(?:workflows|activities|\\.fluidgrids)/(?:${SEGMENT}/)*${SEGMENT}\\.ts|workflows/(?:${SEGMENT}/)*${SEGMENT}\\.(?:layout|pinned)\\.json)$`
|
|
202
|
+
);
|
|
203
|
+
var PYTHON_MODULE_PATH = new RegExp(
|
|
204
|
+
`^workflows/(?:${SEGMENT}/)*${SEGMENT}\\.py$`
|
|
205
|
+
);
|
|
206
|
+
var ImportSourceError = class extends Error {
|
|
207
|
+
constructor(problem) {
|
|
208
|
+
super(`Import source refused: ${problem.code}`);
|
|
209
|
+
this.problem = problem;
|
|
210
|
+
this.name = "ImportSourceError";
|
|
211
|
+
}
|
|
212
|
+
problem;
|
|
213
|
+
};
|
|
214
|
+
var encoder = new TextEncoder();
|
|
215
|
+
function utf8Length(value) {
|
|
216
|
+
return encoder.encode(value).length;
|
|
217
|
+
}
|
|
218
|
+
function isTypeScriptModule(path) {
|
|
219
|
+
return path.startsWith("workflows/") && path.endsWith(".ts") && !path.endsWith(".d.ts") && !/\.(test|spec)\.ts$/.test(path);
|
|
220
|
+
}
|
|
221
|
+
function assertImportByteSize(bytes, limitBytes) {
|
|
222
|
+
if (bytes > limitBytes) {
|
|
223
|
+
throw new ImportSourceError({ code: "too-large", limitBytes });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function typeScriptModuleFile(fileName, content) {
|
|
227
|
+
const stem = safeSegment(fileBaseName(fileName).replace(/\.ts$/i, ""));
|
|
228
|
+
return { path: `workflows/${stem || "workflow"}.ts`, content };
|
|
229
|
+
}
|
|
230
|
+
function checkTypeScriptImport(files) {
|
|
231
|
+
if (files.length > MAX_IMPORT_SOURCE_FILES) {
|
|
232
|
+
throw new ImportSourceError({
|
|
233
|
+
code: "too-many-files",
|
|
234
|
+
limit: MAX_IMPORT_SOURCE_FILES
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const bytes = files.reduce((sum, file) => sum + utf8Length(file.content), 0);
|
|
238
|
+
assertImportByteSize(bytes, MAX_IMPORT_SOURCE_BYTES);
|
|
239
|
+
const modules = files.filter((file) => isTypeScriptModule(file.path));
|
|
240
|
+
if (modules.length === 0) throw new ImportSourceError({ code: "no-module" });
|
|
241
|
+
if (!files.some((file) => file.path === NODE_CLIENTS_PATH)) {
|
|
242
|
+
const importer = files.find(
|
|
243
|
+
(file) => file.path.endsWith(".ts") && NODE_CLIENTS_IMPORT.test(file.content)
|
|
244
|
+
);
|
|
245
|
+
if (importer) {
|
|
246
|
+
throw new ImportSourceError({
|
|
247
|
+
code: "needs-node-clients",
|
|
248
|
+
path: importer.path
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return [...files];
|
|
253
|
+
}
|
|
254
|
+
async function readProjectArchive(data) {
|
|
255
|
+
assertImportByteSize(data.byteLength, MAX_IMPORT_ARCHIVE_BYTES);
|
|
256
|
+
let zip;
|
|
257
|
+
try {
|
|
258
|
+
zip = await JSZip.loadAsync(data);
|
|
259
|
+
} catch {
|
|
260
|
+
throw new ImportSourceError({ code: "invalid-archive" });
|
|
261
|
+
}
|
|
262
|
+
const entries = Object.values(zip.files).filter((entry) => !entry.dir);
|
|
263
|
+
if (entries.length > MAX_ARCHIVE_ENTRIES) {
|
|
264
|
+
throw new ImportSourceError({
|
|
265
|
+
code: "too-many-files",
|
|
266
|
+
limit: MAX_IMPORT_SOURCE_FILES
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const root = commonRootFolder(entries.map((entry) => entry.name));
|
|
270
|
+
const selected = entries.map((entry) => ({ entry, path: entry.name.slice(root.length) })).filter(({ path }) => PROJECT_SOURCE_PATH.test(path)).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
271
|
+
if (selected.length > MAX_IMPORT_SOURCE_FILES) {
|
|
272
|
+
throw new ImportSourceError({
|
|
273
|
+
code: "too-many-files",
|
|
274
|
+
limit: MAX_IMPORT_SOURCE_FILES
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (!selected.some(({ path }) => isTypeScriptModule(path))) {
|
|
278
|
+
const python = entries.some(
|
|
279
|
+
(entry) => PYTHON_MODULE_PATH.test(entry.name.slice(root.length))
|
|
280
|
+
);
|
|
281
|
+
throw new ImportSourceError({
|
|
282
|
+
code: python ? "python-needs-cli" : "no-module"
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
const files = [];
|
|
286
|
+
let bytes = 0;
|
|
287
|
+
for (const { entry, path } of selected) {
|
|
288
|
+
const content = await entry.async("string");
|
|
289
|
+
bytes += utf8Length(content);
|
|
290
|
+
assertImportByteSize(bytes, MAX_IMPORT_SOURCE_BYTES);
|
|
291
|
+
files.push({ path, content });
|
|
292
|
+
}
|
|
293
|
+
return files;
|
|
294
|
+
}
|
|
295
|
+
function commonRootFolder(names) {
|
|
296
|
+
const first = names[0];
|
|
297
|
+
if (!first) return "";
|
|
298
|
+
const head = first.split("/", 1)[0];
|
|
299
|
+
if (head === first || head === "workflows" || head === ".fluidgrids")
|
|
300
|
+
return "";
|
|
301
|
+
const prefix = `${head}/`;
|
|
302
|
+
return names.every((name) => name.startsWith(prefix)) ? prefix : "";
|
|
303
|
+
}
|
|
304
|
+
function typeScriptImportSource(files) {
|
|
305
|
+
return {
|
|
306
|
+
format: "TYPESCRIPT" /* Typescript */,
|
|
307
|
+
files: files.map((file) => ({ path: file.path, content: file.content }))
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function primaryImportModule(files) {
|
|
311
|
+
return files.find((file) => isTypeScriptModule(file.path)) ?? null;
|
|
312
|
+
}
|
|
313
|
+
function workflowNameFromFile(fileName) {
|
|
314
|
+
const stem = fileBaseName(fileName).replace(/\.(ts|zip)$/i, "").replace(/[-_.]+/g, " ").trim();
|
|
315
|
+
if (!stem) return "Imported workflow";
|
|
316
|
+
return stem.charAt(0).toUpperCase() + stem.slice(1);
|
|
317
|
+
}
|
|
318
|
+
var WORKFLOW_BODY_START = /\(wf\)\s*=>\s*\{|^\s*def\s+\w+\(wf\)\s*:/;
|
|
319
|
+
function lineOfNode(source, nodeId) {
|
|
320
|
+
const escaped = nodeId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
321
|
+
const pattern = new RegExp(`\\bid\\s*[:=]\\s*["']${escaped}["']`);
|
|
322
|
+
const lines = source.split("\n");
|
|
323
|
+
const bodyStart = Math.max(
|
|
324
|
+
0,
|
|
325
|
+
lines.findIndex((line) => WORKFLOW_BODY_START.test(line))
|
|
326
|
+
);
|
|
327
|
+
for (let index = bodyStart; index < lines.length; index += 1) {
|
|
328
|
+
if (pattern.test(lines[index])) return index + 1;
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/hooks/useWorkflowCodeExport.ts
|
|
334
|
+
async function fetchWorkflowCodeExport(target) {
|
|
335
|
+
const result = await getFluidGridsClient().query({
|
|
336
|
+
query: ExportWorkflowCodeDocument,
|
|
337
|
+
variables: {
|
|
338
|
+
workflowKey: target.workflowKey,
|
|
339
|
+
version: target.version || null,
|
|
340
|
+
// The service defaults (connection ids kept, layout sidecar on, pinned
|
|
341
|
+
// data off) are what the builder wants, so no options are sent.
|
|
342
|
+
format: target.format
|
|
343
|
+
},
|
|
344
|
+
fetchPolicy: "network-only"
|
|
345
|
+
});
|
|
346
|
+
throwIfResultError(result);
|
|
347
|
+
const exported = result.data?.exportWorkflow;
|
|
348
|
+
if (!exported) throw new Error("The export returned no result");
|
|
349
|
+
return exported;
|
|
350
|
+
}
|
|
351
|
+
function useWorkflowCodeExport(target, enabled) {
|
|
352
|
+
const { workflowKey, version, format } = target;
|
|
353
|
+
const [data, setData] = useState(null);
|
|
354
|
+
const [loading, setLoading] = useState(false);
|
|
355
|
+
const [error, setError] = useState(null);
|
|
356
|
+
const request = useRef(0);
|
|
357
|
+
const refetch = useCallback(async () => {
|
|
358
|
+
if (!workflowKey) return;
|
|
359
|
+
const id = ++request.current;
|
|
360
|
+
setLoading(true);
|
|
361
|
+
setError(null);
|
|
362
|
+
try {
|
|
363
|
+
const exported = await fetchWorkflowCodeExport({
|
|
364
|
+
workflowKey,
|
|
365
|
+
version,
|
|
366
|
+
format
|
|
367
|
+
});
|
|
368
|
+
if (id === request.current) setData(exported);
|
|
369
|
+
} catch (err) {
|
|
370
|
+
if (id === request.current) {
|
|
371
|
+
setData(null);
|
|
372
|
+
setError(describeOperationError(err, "The export failed"));
|
|
373
|
+
}
|
|
374
|
+
} finally {
|
|
375
|
+
if (id === request.current) setLoading(false);
|
|
376
|
+
}
|
|
377
|
+
}, [workflowKey, version, format]);
|
|
378
|
+
useEffect(() => {
|
|
379
|
+
if (!enabled) return;
|
|
380
|
+
setData(null);
|
|
381
|
+
void refetch();
|
|
382
|
+
}, [enabled, refetch]);
|
|
383
|
+
return { data, loading, error, refetch };
|
|
384
|
+
}
|
|
385
|
+
async function downloadWorkflowCodeExport(exported, format, save = downloadBlob) {
|
|
386
|
+
const { filename, blob } = await buildExportDownload(exported, format);
|
|
387
|
+
save(filename, blob);
|
|
388
|
+
}
|
|
389
|
+
function useExportWorkflowAs(save = downloadBlob) {
|
|
390
|
+
const [exporting, setExporting] = useState(null);
|
|
391
|
+
const exportAs = useCallback(
|
|
392
|
+
async (target) => {
|
|
393
|
+
setExporting(target.format);
|
|
394
|
+
try {
|
|
395
|
+
const exported = await fetchWorkflowCodeExport(target);
|
|
396
|
+
await downloadWorkflowCodeExport(exported, target.format, save);
|
|
397
|
+
return exported;
|
|
398
|
+
} finally {
|
|
399
|
+
setExporting(null);
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
[save]
|
|
403
|
+
);
|
|
404
|
+
return { exportAs, exporting };
|
|
405
|
+
}
|
|
406
|
+
function QuotaExhaustedDialog({
|
|
407
|
+
open,
|
|
408
|
+
onOpenChange,
|
|
409
|
+
onUpgrade,
|
|
410
|
+
title,
|
|
411
|
+
description,
|
|
412
|
+
surface,
|
|
413
|
+
limitHit = true
|
|
414
|
+
}) {
|
|
415
|
+
const tr = useTr();
|
|
416
|
+
const { bus, workspaceId } = useFluidGridsAnalytics();
|
|
417
|
+
const wasOpen = useRef(false);
|
|
418
|
+
useEffect(() => {
|
|
419
|
+
if (open && !wasOpen.current && limitHit) {
|
|
420
|
+
emitWorkflowLimitHit(bus, { workspaceId, surface });
|
|
421
|
+
}
|
|
422
|
+
wasOpen.current = open;
|
|
423
|
+
}, [open, bus, workspaceId, surface, limitHit]);
|
|
424
|
+
const handleUpgrade = () => {
|
|
425
|
+
emitUpgradeClicked(bus, {
|
|
426
|
+
workspaceId,
|
|
427
|
+
surface: "quota-dialog",
|
|
428
|
+
quotaKey: WORKFLOWS_QUOTA_NAME
|
|
429
|
+
});
|
|
430
|
+
onUpgrade();
|
|
431
|
+
};
|
|
432
|
+
const resolvedTitle = title ?? tr(
|
|
433
|
+
"fluidgrids.components.quotaExhaustedDialog.title",
|
|
434
|
+
"Workflow Limit Reached"
|
|
435
|
+
);
|
|
436
|
+
const resolvedDescription = description ?? tr(
|
|
437
|
+
"fluidgrids.components.quotaExhaustedDialog.description",
|
|
438
|
+
"You've reached the workflow limit for your plan. Upgrade your plan to create more workflows."
|
|
439
|
+
);
|
|
440
|
+
return /* @__PURE__ */ jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
441
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
442
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: resolvedTitle }),
|
|
443
|
+
/* @__PURE__ */ jsx(AlertDialogDescription, { children: resolvedDescription })
|
|
444
|
+
] }),
|
|
445
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
|
|
446
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { children: tr(
|
|
447
|
+
"fluidgrids.components.quotaExhaustedDialog.dismiss",
|
|
448
|
+
"Dismiss"
|
|
449
|
+
) }),
|
|
450
|
+
/* @__PURE__ */ jsx(AlertDialogAction, { onClick: handleUpgrade, children: tr(
|
|
451
|
+
"fluidgrids.components.quotaExhaustedDialog.viewPlans",
|
|
452
|
+
"View plans"
|
|
453
|
+
) })
|
|
454
|
+
] })
|
|
455
|
+
] }) });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export { CODE_EXPORT_FORMATS, ImportSourceError, MAX_IMPORT_ARCHIVE_BYTES, MAX_IMPORT_SOURCE_BYTES, QuotaExhaustedDialog, assertImportByteSize, checkTypeScriptImport, downloadWorkflowCodeExport, downloadWorkflowExport, fileBaseName, importAcceptAttribute, importFileKind, isLosslessReport, isLossyFidelityItem, lineOfNode, orderedExportFiles, parseWorkflowImport, primaryImportModule, readProjectArchive, sortFidelityItems, typeScriptImportSource, typeScriptModuleFile, useExportWorkflowAs, useWorkflowCodeExport, workflowNameFromFile };
|
|
@@ -2,7 +2,7 @@ import { cn as cn$1 } from './chunk-JL7CASN5.js';
|
|
|
2
2
|
import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-PXIFUMLA.js';
|
|
3
3
|
import { getFluidGridsConfig, validateTrustedOrigin, resetFluidGridsExecutionState, setFluidGridsConfig, resetFluidGridsConfig } from './chunk-CY6KX4RG.js';
|
|
4
4
|
import { GetMyNodeInstallationsDocument } from './chunk-JUUYPCFY.js';
|
|
5
|
-
import { fetchAllNodeEndpointConfigs, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowDefinitionDocument, WorkflowRunDocument } from './chunk-
|
|
5
|
+
import { fetchAllNodeEndpointConfigs, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowDefinitionDocument, WorkflowRunDocument } from './chunk-7PBOU7JC.js';
|
|
6
6
|
import { useTr, ENGLISH_FALLBACK_TRANSLATOR, FluidGridsTranslationProvider } from './chunk-UULVIT25.js';
|
|
7
7
|
import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-NTWG3CJX.js';
|
|
8
8
|
import { CombinedGraphQLErrors, ServerError } from '@apollo/client/errors';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FailureTriagePanel } from './chunk-
|
|
2
|
-
import { useDataSinks, useDataSinkData, useInsightsData, useRunReplayActions } from './chunk-
|
|
3
|
-
import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-
|
|
1
|
+
import { FailureTriagePanel } from './chunk-SQ6VSXFI.js';
|
|
2
|
+
import { useDataSinks, useDataSinkData, useInsightsData, useRunReplayActions } from './chunk-VFPIMW52.js';
|
|
3
|
+
import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-NVUJFHTQ.js';
|
|
4
4
|
import { useTr } from './chunk-UULVIT25.js';
|
|
5
5
|
import { useState, useMemo, useEffect } from 'react';
|
|
6
6
|
import { Icon } from '@iconify/react';
|