@burdenoff/fluidgrids-fe-sdk 2026.1113.1 → 2026.1113.2
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/{AiStudioPage-D3MAk-bs.js → AiStudioPage-5KYZ8r8-.js} +464 -7
- package/dist/{ApprovalInboxPage-Bb8H9ruw.js → ApprovalInboxPage-BsJX4osU.js} +4 -3
- package/dist/{ConnectionFormDialog-Bg9_OdHX.js → ConnectionFormDialog-Dr1-CTan.js} +2 -1
- package/dist/CreateFluidgridsTagDocument-Dtp9KPLJ.js +1055 -0
- package/dist/{DataSinkDashboardPage-Dc2QuJbL.js → DataSinkDashboardPage-8ZDn85e2.js} +1 -1
- package/dist/{DataTablesPage-Cv4veaKK.js → DataTablesPage-BBvABuxE.js} +5 -4
- package/dist/{EntityTagPicker-D8ws706D.js → EntityTagPicker-BppBSEJk.js} +2 -2
- package/dist/{ExecutionDetailPage-CTVGE-d-.js → ExecutionDetailPage-BXDF2eoT.js} +1444 -18
- package/dist/ExecutionsListDocument-CzdLPpj5.js +1259 -0
- package/dist/{ExecutionsListPage-J0sJPuoM.js → ExecutionsListPage--4BthM8L.js} +285 -11
- package/dist/FailureTriagePanel-B5WCSRFo.js +1252 -0
- package/dist/{FluidGridsProvider-C4FVNz0n.js → FluidGridsProvider-Dc_Jhtch.js} +8666 -8734
- package/dist/{HistoryPage-B9XcTPrO.js → HistoryPage-BvFk2m1m.js} +4 -3
- package/dist/{InsightsPage-DFOltjyj.js → InsightsPage-Dwmb02F6.js} +2682 -5
- package/dist/{MissingNodesInstallModal-D8H-YNCJ.js → MissingNodesInstallModal-Cze674hp.js} +2 -2
- package/dist/{NodesPage-YQ-hP60x.js → NodesPage-C5Dq-ru-.js} +3 -2
- package/dist/{OpenTemplateAsCodeButton-hH26HQCk.js → OpenTemplateAsCodeButton-8MZHih6I.js} +2 -2
- package/dist/{PaidTemplateConfirm-DoKWMBtR.js → PaidTemplateConfirm-0G-gv29n.js} +1 -1
- package/dist/{PublishCustomNodeAction-5XXnSA3H.js → PublishCustomNodeAction-OOY35dUv.js} +1 -1
- package/dist/QuotaExhaustedDialog-wvu_-WJ-.js +55 -0
- package/dist/{RunnerStatusPill-Ctg4TJcP.js → RunnerStatusPill-Bh7mEsPh.js} +2 -1
- package/dist/{RunnersPage-CAt4YDhB.js → RunnersPage-DHeuUDil.js} +2 -2
- package/dist/{SettingsPage-DEKPCVVP.js → SettingsPage-CfjXiGpb.js} +694 -14
- package/dist/{TagFilterSelect-CwM2-DU6.js → TagFilterSelect-CAShgvbS.js} +1 -1
- package/dist/{TaskQueuesPage-Dhi6SIT4.js → TaskQueuesPage--Mmn36b-.js} +3 -3
- package/dist/{TemplateCard-DVSd_4zJ.js → TemplateCard-BA_9em22.js} +644 -352
- package/dist/{TemplateConfigurationModal-BKi1z7z7.js → TemplateConfigurationModal-BT3PaR0O.js} +4 -3
- package/dist/{TemplateDetailPanel-DK1gF7P5.js → TemplateDetailPanel-BxFNnlXm.js} +2 -1
- package/dist/{TemplatePreview-DuSX2Q7H.js → TemplatePreview-Bagja6WN.js} +7 -7
- package/dist/{TemplatesPage-CAWHh61l.js → TemplatesPage-CAajpJ5w.js} +711 -6
- package/dist/{VariablesPage-D7CoatWf.js → VariablesPage-CgUBKhAe.js} +3 -3
- package/dist/{VersionDiffDialog-DGwysiyE.js → VersionDiffDialog-D9W5Fm56.js} +233 -129
- package/dist/VersionLifecycleBar-DuphLOT0.js +131 -0
- package/dist/{WorkflowBuilder-DAeoQi5E.js → WorkflowBuilder-DMpSPElu.js} +16 -13
- package/dist/{WorkflowBuilder-BJgqDboZ.js → WorkflowBuilder-v8h8skwG.js} +5 -5
- package/dist/WorkflowDefinitionDocument-DZLbTPUZ.js +321 -0
- package/dist/WorkflowEditLeaseStatusDocument-Dp62med3.js +627 -0
- package/dist/WorkflowProposalCard-zCdUpCk8.js +249 -0
- package/dist/WorkflowProposalDocument-BdUbsrd0.js +620 -0
- package/dist/{WorkflowSearchRedirect-CMEvd_AR.js → WorkflowSearchRedirect-G78lINAE.js} +1 -1
- package/dist/{WorkflowShareDialog-BgHRsDP0.js → WorkflowShareDialog-B7VOgr4-.js} +304 -3
- package/dist/WorkflowVersionsDocument-CfPMloRW.js +3195 -0
- package/dist/{WorkflowsList-CcQPxWKL.js → WorkflowsList-CPYzfE4K.js} +11 -9
- package/dist/agent-form-D0uLvzx4.js +718 -0
- package/dist/{agentTools-DKpNKztf.js → agentTools-DTXUqtfu.js} +4 -4
- package/dist/api-BZVyX5d2.js +2286 -0
- package/dist/api-BqA-f-AD.js +8064 -0
- package/dist/app-BJUvugPM.js +3813 -0
- package/dist/assert-rules-CPNrO44K.js +318 -0
- package/dist/attach-node-helpers-CNsYtLOR.js +32 -0
- package/dist/badge-CXfPhoMH.js +26 -0
- package/dist/{canvasNoteTools-C_AN79m0.js → canvasNoteTools-DmfZwtnL.js} +3 -3
- package/dist/{channel-CZ70oI81.js → channel-DtF1n_ab.js} +1 -1
- package/dist/client-Ctl4jmY3.js +127 -0
- package/dist/code-editor-diagnostics-Zr0cIcx1.js +46 -0
- package/dist/{code-editor-field-Dt6KNWwN.js → code-editor-field-Cv907s3W.js} +2 -2
- package/dist/{codec-client-CJ95Rmqo.js → codec-client-CAYl6j46.js} +2 -1
- package/dist/conditionBuilder-D8aLGL1w.js +1134 -0
- package/dist/config-X97zrIvn.js +600 -0
- package/dist/{definition-canvas-Bshn_dSP.js → definition-canvas-DL25_6NH.js} +1 -1
- package/dist/{dialog-Cjb4lB4D.js → dialog-BVd152GS.js} +3 -14
- package/dist/durable-nodes-DXCCQYWD.js +81 -0
- package/dist/errors-xPZ-IbQd.js +142 -0
- package/dist/executions.js +2 -2
- package/dist/{flow-builder-D3Hl2AFk.js → flow-builder-Dv5fWJ6F.js} +9301 -8189
- package/dist/{node-registry-refresh-Dy0uRg9f.js → global-client-DP_Wxe5t.js} +2 -27
- package/dist/global-operations-7pvXw_j9.js +145 -0
- package/dist/history.js +1 -1
- package/dist/{index-CWcP-tG1.d.ts → index-DtMUtXNq.d.ts} +247 -247
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4403 -46
- package/dist/input-ChT6Q-Pr.js +17 -0
- package/dist/logs-panel-DtG93ocy.js +342 -0
- package/dist/mapping-colors-BIX_YeCQ.js +48 -0
- package/dist/{node-detail-view-bVLeYarT.js → node-detail-view-BKI_2kM6.js} +12 -11
- package/dist/{node-editor-tab-body-JHHVtQgR.js → node-editor-tab-body-Cc1MWEFR.js} +3 -3
- package/dist/{node-operation-selection-DAbS6jSY.js → node-operation-selection-Czeeb2D4.js} +2 -139
- package/dist/node-registry-refresh-s3okf83X.js +27 -0
- package/dist/nodes.js +1 -1
- package/dist/notesKey-5EaREBGA.js +12 -0
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/{popover-Xx3gN7vg.js → popover-DDv7J1Kl.js} +717 -6
- package/dist/{quick-config-panel-BvVQXuIL.js → quick-config-panel-BPCRfqWI.js} +4 -4
- package/dist/read-only-context-Dt9N9SIG.js +18 -0
- package/dist/{schedule-panel-DCb40TPN.js → schedule-panel-C1KwfLCE.js} +14 -12
- package/dist/settings.js +1 -1
- package/dist/{shared-CMfn30xT.js → shared-Ctl9ZHo1.js} +2 -1
- package/dist/{sidebar-node-editor-DxCWFZG2.js → sidebar-node-editor-d9Rlq5pU.js} +4 -4
- package/dist/sticky-notes-23j-wL6Q.js +96 -0
- package/dist/template-code-dqRunGOv.js +184 -0
- package/dist/template-preview-C01KV277.js +29 -0
- package/dist/templates.js +2 -2
- package/dist/token-refresh-link-BjN7H4qA.js +291 -0
- package/dist/useAgentDefinitions-ZKX2N9aP.js +3101 -0
- package/dist/useConnections-BlB0gbOB.js +931 -0
- package/dist/useDurableParity-BgLH_SCF.js +2088 -0
- package/dist/{useEntityTags-Db4Pc8_M.js → useEntityTags-4iW6UTly.js} +227 -3
- package/dist/{useExecutions-DOpG1pUQ.js → useExecutions-Bm7q_UwN.js} +6 -3
- package/dist/useGlobalStoreClient-DMTrywdX.js +24 -0
- package/dist/{useHumanRequests-ZiR0sSzS.js → useHumanRequests-DwG2tQ3f.js} +2 -1
- package/dist/{useObservability-rQmwhoZZ.js → useObservability-DPATdbw7.js} +4 -2
- package/dist/{useRunHistory-COn6dl7l.js → useRunHistory-CGfjCsJ3.js} +1122 -4
- package/dist/{useVariables-2ddyJX4a.js → useVariables-DFdZofLQ.js} +2 -1
- package/dist/{useWorkflowCodeExport-ZoUJKRSK.js → useWorkflowCodeExport-D_mCuhKP.js} +289 -4
- package/dist/useWorkflowOrganization-r5FWNBs6.js +1690 -0
- package/dist/useWorkflowProposal-Bh2_qjmM.js +7478 -0
- package/dist/{useWorkflows-jOW60YmD.js → useWorkflows-aqVD7kGa.js} +694 -4
- package/dist/workflow-builder.js +5 -5
- package/dist/{workflow-code-transfer-l9wO34S0.js → workflow-code-transfer-5C0HUzae.js} +1 -53
- package/dist/workflow-host.js +4 -2
- package/dist/workflow-pages.d.ts +1 -1
- package/dist/workflow-pages.js +19 -19
- package/dist/workflow-preview.js +3 -2
- package/dist/workflows-list.js +1 -1
- package/package.json +1 -1
- package/dist/FailureTriagePanel-BbYFYeV_.js +0 -311
- package/dist/WorkflowProposalCard-DGMdZC4R.js +0 -1432
- package/dist/agent-form-Bfvm32j1.js +0 -83
- package/dist/api-DQtHXbIg.js +0 -224
- package/dist/api-MeAm6g7E.js +0 -55
- package/dist/client-C9XSzSXr.js +0 -1147
- package/dist/graphql-operation-types-C9qbvRa8.js +0 -53122
- package/dist/useAgentDefinitions-_hgNCK1A.js +0 -550
- package/dist/useConnections-Cs7LCWEM.js +0 -401
- package/dist/useDurableParity-CcLeCDfv.js +0 -504
- package/dist/useWorkflowOrganization-QxVePSkk.js +0 -282
|
@@ -0,0 +1,3813 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { a as mappingTypeColor, i as compatibilityColor, n as MAPPING_FALLBACK_COLOR, r as MAPPING_TYPE_COLORS, t as MAPPING_COMPATIBILITY_COLORS } from "./mapping-colors-BIX_YeCQ.js";
|
|
3
|
+
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
4
|
+
import * as React$1 from "react";
|
|
5
|
+
import { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { Background, Controls, Handle, MiniMap, Panel, Position, ReactFlow, ReactFlowProvider, addEdge, getBezierPath, useEdgesState, useNodesState } from "@xyflow/react";
|
|
7
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster } from "@burdenoff/fe-libs/ui";
|
|
9
|
+
import { ThemeProvider } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
10
|
+
import "@xyflow/react/dist/style.css";
|
|
11
|
+
import { AlertCircle, Braces, CheckCircle2, ChevronDown, ChevronRight, Code, Hash, List, PlusCircle, Type, Workflow, X } from "lucide-react";
|
|
12
|
+
import { createPortal } from "react-dom";
|
|
13
|
+
import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLUnionType, buildSchema } from "graphql";
|
|
14
|
+
|
|
15
|
+
// src/builder/components/mapper/theme-provider.tsx
|
|
16
|
+
function ThemeProvider$1({ children, defaultTheme = "system", storageKey = "vite-ui-theme" }) {
|
|
17
|
+
React$1.useEffect(() => {
|
|
18
|
+
if (typeof window !== "undefined") try {
|
|
19
|
+
localStorage.removeItem(storageKey);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.warn("Failed to clean up legacy theme storage:", err);
|
|
22
|
+
}
|
|
23
|
+
}, [storageKey]);
|
|
24
|
+
return /* @__PURE__ */ jsx(ThemeProvider, {
|
|
25
|
+
defaultMode: defaultTheme,
|
|
26
|
+
children
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/builder/components/mapper/config/data.ts
|
|
31
|
+
let parsedSchemas = {};
|
|
32
|
+
let nodeTypes$1 = null;
|
|
33
|
+
let typeCompatibility = null;
|
|
34
|
+
let sampleData = null;
|
|
35
|
+
let nodeStyles = null;
|
|
36
|
+
let currentConfig = null;
|
|
37
|
+
function initializeConfig(schemas, dataConfig) {
|
|
38
|
+
parsedSchemas = schemas;
|
|
39
|
+
currentConfig = dataConfig;
|
|
40
|
+
generateNodeTypes(dataConfig);
|
|
41
|
+
generateTypeCompatibilityFunc(dataConfig);
|
|
42
|
+
generateSampleDataFunc(dataConfig);
|
|
43
|
+
generateVisualStylesFunc(dataConfig);
|
|
44
|
+
}
|
|
45
|
+
function generateNodeTypes(dataConfig) {
|
|
46
|
+
const newNodeTypes = {
|
|
47
|
+
sourceNodes: {},
|
|
48
|
+
targetNodes: {}
|
|
49
|
+
};
|
|
50
|
+
Object.entries(dataConfig.nodes).forEach(([nodeKey, nodeInfo]) => {
|
|
51
|
+
const category = nodeInfo.category;
|
|
52
|
+
if (category === "source" || category === "both") newNodeTypes.sourceNodes[nodeKey] = {
|
|
53
|
+
displayName: nodeInfo.displayName,
|
|
54
|
+
schema: nodeKey,
|
|
55
|
+
inputTypes: [],
|
|
56
|
+
operations: []
|
|
57
|
+
};
|
|
58
|
+
if (category === "target" || category === "both") newNodeTypes.targetNodes[nodeKey] = {
|
|
59
|
+
displayName: nodeInfo.displayName,
|
|
60
|
+
schema: nodeKey,
|
|
61
|
+
inputTypes: [],
|
|
62
|
+
operations: []
|
|
63
|
+
};
|
|
64
|
+
if (!parsedSchemas[nodeKey]) {}
|
|
65
|
+
const schema = parsedSchemas[nodeKey];
|
|
66
|
+
if (schema) {
|
|
67
|
+
const operations = findOperationsWithStateArgs(schema);
|
|
68
|
+
const inputTypes = operations.map((op) => op.stateType).filter((type) => type !== null);
|
|
69
|
+
if (category === "both") {
|
|
70
|
+
newNodeTypes.sourceNodes[nodeKey].inputTypes = [...new Set(inputTypes)];
|
|
71
|
+
newNodeTypes.sourceNodes[nodeKey].operations = operations;
|
|
72
|
+
newNodeTypes.targetNodes[nodeKey].inputTypes = [...new Set(inputTypes)];
|
|
73
|
+
newNodeTypes.targetNodes[nodeKey].operations = operations;
|
|
74
|
+
} else if (category === "source") {
|
|
75
|
+
newNodeTypes.sourceNodes[nodeKey].inputTypes = [...new Set(inputTypes)];
|
|
76
|
+
newNodeTypes.sourceNodes[nodeKey].operations = operations;
|
|
77
|
+
} else if (category === "target") {
|
|
78
|
+
newNodeTypes.targetNodes[nodeKey].inputTypes = [...new Set(inputTypes)];
|
|
79
|
+
newNodeTypes.targetNodes[nodeKey].operations = operations;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
nodeTypes$1 = newNodeTypes;
|
|
84
|
+
}
|
|
85
|
+
function findOperationsWithStateArgs(schema) {
|
|
86
|
+
const operations = [];
|
|
87
|
+
if (schema.queryType) {
|
|
88
|
+
const queryType = schema.types[schema.queryType];
|
|
89
|
+
if (queryType && queryType.fields) for (const fieldName in queryType.fields) {
|
|
90
|
+
const field = queryType.fields[fieldName];
|
|
91
|
+
if (fieldName === "__schema" || fieldName === "__type") continue;
|
|
92
|
+
const stateArg = field.args && field.args.find((arg) => arg.name === "state");
|
|
93
|
+
const contextArg = field.args && field.args.find((arg) => arg.name === "context");
|
|
94
|
+
if (!!stateArg && !!contextArg) operations.push({
|
|
95
|
+
name: fieldName,
|
|
96
|
+
type: "query",
|
|
97
|
+
returnType: field.type,
|
|
98
|
+
stateType: stateArg.type,
|
|
99
|
+
inputType: stateArg.type
|
|
100
|
+
});
|
|
101
|
+
else if (field.args && field.args.length > 0) {
|
|
102
|
+
const firstArg = field.args[0];
|
|
103
|
+
operations.push({
|
|
104
|
+
name: fieldName,
|
|
105
|
+
type: "query",
|
|
106
|
+
returnType: field.type,
|
|
107
|
+
stateType: firstArg.type,
|
|
108
|
+
inputType: firstArg.type
|
|
109
|
+
});
|
|
110
|
+
} else operations.push({
|
|
111
|
+
name: fieldName,
|
|
112
|
+
type: "query",
|
|
113
|
+
returnType: field.type,
|
|
114
|
+
stateType: null,
|
|
115
|
+
inputType: null
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else console.warn(`Query type ${schema.queryType} not found or has no fields`);
|
|
119
|
+
}
|
|
120
|
+
if (schema.mutationType) {
|
|
121
|
+
const mutationType = schema.types[schema.mutationType];
|
|
122
|
+
if (mutationType && mutationType.fields) for (const fieldName in mutationType.fields) {
|
|
123
|
+
const field = mutationType.fields[fieldName];
|
|
124
|
+
const stateArg = field.args && field.args.find((arg) => arg.name === "state");
|
|
125
|
+
const contextArg = field.args && field.args.find((arg) => arg.name === "context");
|
|
126
|
+
if (!!stateArg && !!contextArg) operations.push({
|
|
127
|
+
name: fieldName,
|
|
128
|
+
type: "mutation",
|
|
129
|
+
returnType: field.type,
|
|
130
|
+
stateType: stateArg.type,
|
|
131
|
+
inputType: stateArg.type
|
|
132
|
+
});
|
|
133
|
+
else if (field.args && field.args.length > 0) {
|
|
134
|
+
const firstArg = field.args[0];
|
|
135
|
+
operations.push({
|
|
136
|
+
name: fieldName,
|
|
137
|
+
type: "mutation",
|
|
138
|
+
returnType: field.type,
|
|
139
|
+
stateType: firstArg.type,
|
|
140
|
+
inputType: firstArg.type
|
|
141
|
+
});
|
|
142
|
+
} else operations.push({
|
|
143
|
+
name: fieldName,
|
|
144
|
+
type: "mutation",
|
|
145
|
+
returnType: field.type,
|
|
146
|
+
stateType: null,
|
|
147
|
+
inputType: null
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else console.warn(`Mutation type ${schema.mutationType} not found or has no fields`);
|
|
151
|
+
}
|
|
152
|
+
return operations;
|
|
153
|
+
}
|
|
154
|
+
function generateTypeCompatibilityFunc(dataConfig) {
|
|
155
|
+
typeCompatibility = dataConfig.typeCompatibility;
|
|
156
|
+
}
|
|
157
|
+
function generateSampleDataFunc(dataConfig) {
|
|
158
|
+
sampleData = dataConfig.sampleData;
|
|
159
|
+
}
|
|
160
|
+
function generateVisualStylesFunc(dataConfig) {
|
|
161
|
+
const newNodeStyles = {};
|
|
162
|
+
Object.entries(dataConfig.nodes).forEach(([nodeKey, nodeInfo]) => {
|
|
163
|
+
newNodeStyles[nodeKey] = nodeInfo.style || {};
|
|
164
|
+
});
|
|
165
|
+
nodeStyles = newNodeStyles;
|
|
166
|
+
}
|
|
167
|
+
function getEdgeColor(mappingType, compatible = true) {
|
|
168
|
+
const colorConfig = currentConfig?.mappingTypes[mappingType]?.color;
|
|
169
|
+
if (!colorConfig) return null;
|
|
170
|
+
return typeof colorConfig === "string" ? colorConfig : compatible ? colorConfig.compatible : colorConfig.incompatible;
|
|
171
|
+
}
|
|
172
|
+
function getReturnTypeForOperation(nodeType, operationName) {
|
|
173
|
+
return ((nodeTypes$1?.targetNodes[nodeType] || nodeTypes$1?.sourceNodes[nodeType])?.operations?.find((op) => op.name === operationName))?.returnType;
|
|
174
|
+
}
|
|
175
|
+
function getNodeStyle(nodeType, side) {
|
|
176
|
+
if (!nodeStyles || !nodeStyles[nodeType]) return "";
|
|
177
|
+
const styles = nodeStyles[nodeType];
|
|
178
|
+
if (side) return (side === "source" ? styles.source : styles.target) || "";
|
|
179
|
+
return styles;
|
|
180
|
+
}
|
|
181
|
+
function getInputTypeForOperation(nodeType, operationName) {
|
|
182
|
+
return ((nodeTypes$1?.targetNodes[nodeType] || nodeTypes$1?.sourceNodes[nodeType])?.operations?.find((op) => op.name === operationName))?.inputType;
|
|
183
|
+
}
|
|
184
|
+
function getTypeCompatibility() {
|
|
185
|
+
return typeCompatibility || {};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/builder/components/lib/type-utils.ts
|
|
189
|
+
function areTypesCompatible(sourceType, targetType) {
|
|
190
|
+
const cleanSourceType = sourceType.replace(/!$/, "").replace(/^\[/, "").replace(/\]!?$/, "");
|
|
191
|
+
const cleanTargetType = targetType.replace(/!$/, "").replace(/\]!?$/, "");
|
|
192
|
+
if (cleanSourceType === cleanTargetType) return true;
|
|
193
|
+
return getTypeCompatibility()[cleanSourceType]?.includes(cleanTargetType) || false;
|
|
194
|
+
}
|
|
195
|
+
function getReadableTypeName(type) {
|
|
196
|
+
const isNonNull = type.endsWith("!");
|
|
197
|
+
const isList = type.includes("[");
|
|
198
|
+
let result = type.replace(/!$/, "").replace(/^\[/, "").replace(/\]!?$/, "");
|
|
199
|
+
if (isList) result = `Array of ${result}`;
|
|
200
|
+
if (isNonNull) result = `${result} (Required)`;
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/builder/components/mapper/schema-node.tsx
|
|
205
|
+
const SchemaNode = memo(({ data, isConnectable }) => {
|
|
206
|
+
const { label, type, side, isRequired, isNested, isArray, nodeType, category } = data;
|
|
207
|
+
const readableType = getReadableTypeName(type);
|
|
208
|
+
const borderColor = getNodeStyle(nodeType || "", side);
|
|
209
|
+
const bgColor = side === "source" ? "bg-surface hover:bg-[var(--color-bg-accent)]/10 transition-colors duration-200" : "bg-surface hover:bg-[var(--color-status-success-bg-subtle)] transition-colors duration-200";
|
|
210
|
+
const categoryColor = getCategoryColor(category);
|
|
211
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
212
|
+
className: `w-56 shadow-sm ${borderColor} ${bgColor} transition-colors duration-200`,
|
|
213
|
+
children: [
|
|
214
|
+
/* @__PURE__ */ jsxs("div", {
|
|
215
|
+
className: `flex items-center px-2 py-1 border-b ${categoryColor}`,
|
|
216
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
217
|
+
className: "flex-1 font-medium text-xs truncate",
|
|
218
|
+
title: label,
|
|
219
|
+
children: label
|
|
220
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
221
|
+
className: "flex gap-1 flex-shrink-0",
|
|
222
|
+
children: [
|
|
223
|
+
isRequired && /* @__PURE__ */ jsx(Badge, {
|
|
224
|
+
variant: "destructive",
|
|
225
|
+
className: "text-[10px] px-1 py-0",
|
|
226
|
+
children: "Req"
|
|
227
|
+
}),
|
|
228
|
+
isArray && /* @__PURE__ */ jsx(Badge, {
|
|
229
|
+
variant: "secondary",
|
|
230
|
+
className: "text-[10px] px-1 py-0",
|
|
231
|
+
children: "Array"
|
|
232
|
+
}),
|
|
233
|
+
isNested && /* @__PURE__ */ jsx(Badge, {
|
|
234
|
+
variant: "outline",
|
|
235
|
+
className: "text-[10px] px-1 py-0",
|
|
236
|
+
children: "Obj"
|
|
237
|
+
})
|
|
238
|
+
]
|
|
239
|
+
})]
|
|
240
|
+
}),
|
|
241
|
+
/* @__PURE__ */ jsx(CardContent, {
|
|
242
|
+
className: "p-1 text-[10px] text-muted-foreground",
|
|
243
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
244
|
+
className: "flex justify-between items-center",
|
|
245
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
246
|
+
title: readableType,
|
|
247
|
+
children: readableType
|
|
248
|
+
}), category && /* @__PURE__ */ jsx(Badge, {
|
|
249
|
+
variant: "outline",
|
|
250
|
+
className: "text-[10px] px-1 py-0",
|
|
251
|
+
style: { backgroundColor: getCategoryBgColor(category) },
|
|
252
|
+
children: category
|
|
253
|
+
})]
|
|
254
|
+
})
|
|
255
|
+
}),
|
|
256
|
+
side === "source" ? /* @__PURE__ */ jsx(Handle, {
|
|
257
|
+
type: "source",
|
|
258
|
+
position: Position.Right,
|
|
259
|
+
isConnectable,
|
|
260
|
+
className: "w-2 h-2 bg-accent transition-transform hover:scale-125"
|
|
261
|
+
}) : /* @__PURE__ */ jsx(Handle, {
|
|
262
|
+
type: "target",
|
|
263
|
+
position: Position.Left,
|
|
264
|
+
isConnectable,
|
|
265
|
+
className: "w-2 h-2 bg-[var(--color-status-success-bg)] transition-transform hover:scale-125"
|
|
266
|
+
})
|
|
267
|
+
]
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
function getCategoryColor(category) {
|
|
271
|
+
if (!category) return "";
|
|
272
|
+
switch (category.toLowerCase()) {
|
|
273
|
+
case "input": return "border-[var(--color-border-accent)]/50 bg-[var(--color-bg-accent)]/10";
|
|
274
|
+
case "metadata": return "border-[var(--color-status-warning-border)]/50 bg-[var(--color-status-warning-bg-subtle)]";
|
|
275
|
+
case "context": return "border-[var(--color-status-success-border)]/50 bg-[var(--color-status-success-bg-subtle)]";
|
|
276
|
+
case "output": return "border-[var(--color-border-accent)]/30 bg-[var(--color-bg-accent)]/5";
|
|
277
|
+
case "config": return "border-[var(--color-status-error-border)]/50 bg-[var(--color-status-error-bg-subtle)]";
|
|
278
|
+
default: return "border-default";
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/** Translucent category tint for the field badge (semantic tokens, light + dark). */
|
|
282
|
+
function getCategoryBgColor(category) {
|
|
283
|
+
switch (category.toLowerCase()) {
|
|
284
|
+
case "input": return "var(--color-node-integration-subtle)";
|
|
285
|
+
case "metadata": return "var(--color-status-warning-bgSubtle)";
|
|
286
|
+
case "context": return "var(--color-status-success-bgSubtle)";
|
|
287
|
+
case "output": return "var(--color-status-info-bgSubtle)";
|
|
288
|
+
case "config": return "var(--color-status-error-bgSubtle)";
|
|
289
|
+
default: return "var(--color-bg-sunken)";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
SchemaNode.displayName = "SchemaNode";
|
|
293
|
+
|
|
294
|
+
// src/builder/components/mapper/mapping-edge.tsx
|
|
295
|
+
const MappingEdge = memo(({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, style = {}, markerEnd }) => {
|
|
296
|
+
const [edgePath, labelX, labelY] = getBezierPath({
|
|
297
|
+
sourceX,
|
|
298
|
+
sourceY,
|
|
299
|
+
sourcePosition,
|
|
300
|
+
targetX,
|
|
301
|
+
targetY,
|
|
302
|
+
targetPosition
|
|
303
|
+
});
|
|
304
|
+
const mappingType = data?.mappingType || "direct";
|
|
305
|
+
const isCompatible = data?.isCompatible !== false;
|
|
306
|
+
let mappingLabel = "";
|
|
307
|
+
const mappingColor = getEdgeColor(mappingType, isCompatible) || MAPPING_FALLBACK_COLOR;
|
|
308
|
+
switch (mappingType) {
|
|
309
|
+
case "direct":
|
|
310
|
+
mappingLabel = "Direct";
|
|
311
|
+
break;
|
|
312
|
+
case "expression":
|
|
313
|
+
mappingLabel = "Expression";
|
|
314
|
+
break;
|
|
315
|
+
case "transform":
|
|
316
|
+
mappingLabel = data?.mappingData?.function ? `Transform: ${data.mappingData.function}` : "Transform";
|
|
317
|
+
break;
|
|
318
|
+
case "loop":
|
|
319
|
+
mappingLabel = "Loop";
|
|
320
|
+
break;
|
|
321
|
+
case "cast": mappingLabel = data?.mappingData?.type ? `Cast: ${data.mappingData.type}` : "Cast";
|
|
322
|
+
}
|
|
323
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("path", {
|
|
324
|
+
id,
|
|
325
|
+
style: {
|
|
326
|
+
...style,
|
|
327
|
+
stroke: mappingColor,
|
|
328
|
+
strokeWidth: 2
|
|
329
|
+
},
|
|
330
|
+
className: "react-flow__edge-path",
|
|
331
|
+
d: edgePath,
|
|
332
|
+
markerEnd
|
|
333
|
+
}), /* @__PURE__ */ jsx("foreignObject", {
|
|
334
|
+
width: 120,
|
|
335
|
+
height: 40,
|
|
336
|
+
x: labelX - 60,
|
|
337
|
+
y: labelY - 20,
|
|
338
|
+
className: "overflow-visible",
|
|
339
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
340
|
+
className: "flex items-center justify-center bg-surface border border-default rounded-md shadow-sm px-2 py-1",
|
|
341
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
342
|
+
className: "text-xs font-medium",
|
|
343
|
+
style: { color: mappingColor },
|
|
344
|
+
children: [mappingLabel, !isCompatible && mappingType === "direct" && " ⚠️"]
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
})] });
|
|
348
|
+
});
|
|
349
|
+
MappingEdge.displayName = "MappingEdge";
|
|
350
|
+
|
|
351
|
+
// src/builder/components/mapper/node-group.tsx
|
|
352
|
+
const NodeGroup = memo(({ data, children }) => {
|
|
353
|
+
const { label, nodeType, side } = data;
|
|
354
|
+
const bgColor = side === "source" ? "bg-[var(--color-bg-accent)]/10" : "bg-[var(--color-status-success-bg-subtle)]";
|
|
355
|
+
const borderColor = side === "source" ? "border-[var(--color-border-accent)]" : "border-[var(--color-status-success-border)]";
|
|
356
|
+
const textColor = side === "source" ? "text-accent" : "text-[var(--color-status-success-text)]";
|
|
357
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
358
|
+
className: `px-4 pt-12 pb-4 rounded-lg border-2 ${borderColor} ${bgColor} min-w-[400px] min-h-[600px] shadow-md relative overflow-visible bg-gradient-to-b ${side === "source" ? "from-[var(--color-bg-accent)]/20 to-transparent" : "from-[var(--color-status-success-bg-subtle)]/50 to-transparent"}`,
|
|
359
|
+
style: { minHeight: "600px" },
|
|
360
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
361
|
+
className: `absolute top-0 left-0 right-0 ${textColor} font-semibold p-2 border-b ${borderColor} text-center bg-surface/70 backdrop-blur-sm rounded-t-lg flex items-center justify-center`,
|
|
362
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
363
|
+
className: "text-sm",
|
|
364
|
+
children: [
|
|
365
|
+
label,
|
|
366
|
+
" ",
|
|
367
|
+
/* @__PURE__ */ jsxs("span", {
|
|
368
|
+
className: "text-xs opacity-75",
|
|
369
|
+
children: [
|
|
370
|
+
"(",
|
|
371
|
+
nodeType,
|
|
372
|
+
")"
|
|
373
|
+
]
|
|
374
|
+
})
|
|
375
|
+
]
|
|
376
|
+
})
|
|
377
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
378
|
+
className: "relative w-full h-full overflow-visible",
|
|
379
|
+
children
|
|
380
|
+
})]
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
NodeGroup.displayName = "NodeGroup";
|
|
384
|
+
|
|
385
|
+
// src/builder/components/lib/node-generator.ts
|
|
386
|
+
/**
|
|
387
|
+
* Find the return type for an operation directly from the schema
|
|
388
|
+
*/
|
|
389
|
+
function findReturnTypeFromSchema(schema, operationName) {
|
|
390
|
+
if (schema.queryType && schema.types[schema.queryType]?.fields[operationName]) return schema.types[schema.queryType].fields[operationName].type;
|
|
391
|
+
if (schema.mutationType && schema.types[schema.mutationType]?.fields[operationName]) return schema.types[schema.mutationType].fields[operationName].type;
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
function findInputTypeFromSchema(schema, operationName) {
|
|
395
|
+
let field = null;
|
|
396
|
+
if (schema.queryType && schema.types[schema.queryType]?.fields[operationName]) field = schema.types[schema.queryType].fields[operationName];
|
|
397
|
+
if (!field && schema.mutationType && schema.types[schema.mutationType]?.fields[operationName]) field = schema.types[schema.mutationType].fields[operationName];
|
|
398
|
+
if (!field) return {
|
|
399
|
+
typeName: null,
|
|
400
|
+
useArgsDirectly: false,
|
|
401
|
+
field: null
|
|
402
|
+
};
|
|
403
|
+
const stateArg = field.args?.find((a) => a.name === "state" || a.name === "input");
|
|
404
|
+
if (stateArg) {
|
|
405
|
+
const inputType = schema.types[stateArg.type];
|
|
406
|
+
if (inputType && (inputType.inputFields || inputType.fields)) return {
|
|
407
|
+
typeName: stateArg.type,
|
|
408
|
+
useArgsDirectly: false,
|
|
409
|
+
field
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (field.args && field.args.length > 0) {
|
|
413
|
+
const firstArg = field.args[0];
|
|
414
|
+
const firstArgType = schema.types[firstArg.type];
|
|
415
|
+
if (firstArgType && firstArgType.kind === "input" && (firstArgType.inputFields || firstArgType.fields)) return {
|
|
416
|
+
typeName: firstArg.type,
|
|
417
|
+
useArgsDirectly: false,
|
|
418
|
+
field
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
if (field.args && field.args.length > 0) return {
|
|
422
|
+
typeName: null,
|
|
423
|
+
useArgsDirectly: true,
|
|
424
|
+
field
|
|
425
|
+
};
|
|
426
|
+
return {
|
|
427
|
+
typeName: null,
|
|
428
|
+
useArgsDirectly: false,
|
|
429
|
+
field: null
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Generate schema nodes for visual mapping
|
|
434
|
+
*/
|
|
435
|
+
function generateSchemaNodes(sourceSchema, sourceNode, sourceOperation, targetSchema, targetNode, targetOperation) {
|
|
436
|
+
const nodes = [];
|
|
437
|
+
const edges = [];
|
|
438
|
+
if (targetSchema?.mutationType && targetSchema?.types[targetSchema.mutationType]) {
|
|
439
|
+
const mutationField = targetSchema.types[targetSchema.mutationType].fields?.[targetOperation];
|
|
440
|
+
if (mutationField?.args) {
|
|
441
|
+
for (const arg of mutationField.args) if (targetSchema.types[arg.type]?.inputFields) {}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
let sourceOutputType = null;
|
|
445
|
+
const sourceReturnTypeName = findReturnTypeFromSchema(sourceSchema, sourceOperation);
|
|
446
|
+
if (sourceReturnTypeName) sourceOutputType = sourceSchema.types[sourceReturnTypeName];
|
|
447
|
+
if (!sourceOutputType) for (const typeName of Object.keys(sourceSchema.types)) {
|
|
448
|
+
const type = sourceSchema.types[typeName];
|
|
449
|
+
if (type.kind === "type" && type.fields && Object.keys(type.fields).length > 0) {
|
|
450
|
+
if (typeName === sourceSchema.queryType || typeName === sourceSchema.mutationType || typeName === sourceSchema.subscriptionType) continue;
|
|
451
|
+
sourceOutputType = type;
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
let targetInputType = null;
|
|
456
|
+
let useArgsDirectlyForTarget = false;
|
|
457
|
+
let targetOperationField = null;
|
|
458
|
+
const targetInputResult = findInputTypeFromSchema(targetSchema, targetOperation);
|
|
459
|
+
if (targetInputResult.typeName) targetInputType = targetSchema.types[targetInputResult.typeName];
|
|
460
|
+
else if (targetInputResult.useArgsDirectly && targetInputResult.field) {
|
|
461
|
+
useArgsDirectlyForTarget = true;
|
|
462
|
+
targetOperationField = targetInputResult.field;
|
|
463
|
+
}
|
|
464
|
+
if (!targetInputType && !useArgsDirectlyForTarget) for (const typeName of Object.keys(targetSchema.types)) {
|
|
465
|
+
const type = targetSchema.types[typeName];
|
|
466
|
+
if (type.kind === "input" && (type.inputFields || type.fields)) {
|
|
467
|
+
targetInputType = type;
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const sourceGroupNode = {
|
|
472
|
+
id: "source-group",
|
|
473
|
+
type: "nodeGroup",
|
|
474
|
+
position: {
|
|
475
|
+
x: 50,
|
|
476
|
+
y: 50
|
|
477
|
+
},
|
|
478
|
+
style: {
|
|
479
|
+
width: 450,
|
|
480
|
+
height: 800
|
|
481
|
+
},
|
|
482
|
+
data: {
|
|
483
|
+
label: `Source: ${sourceOperation}`,
|
|
484
|
+
nodeType: sourceNode,
|
|
485
|
+
side: "source"
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
const targetGroupNode = {
|
|
489
|
+
id: "target-group",
|
|
490
|
+
type: "nodeGroup",
|
|
491
|
+
position: {
|
|
492
|
+
x: 650,
|
|
493
|
+
y: 50
|
|
494
|
+
},
|
|
495
|
+
style: {
|
|
496
|
+
width: 450,
|
|
497
|
+
height: 800
|
|
498
|
+
},
|
|
499
|
+
data: {
|
|
500
|
+
label: `Target: ${targetOperation}`,
|
|
501
|
+
nodeType: targetNode,
|
|
502
|
+
side: "target"
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
nodes.push(sourceGroupNode, targetGroupNode);
|
|
506
|
+
const yPos = 70;
|
|
507
|
+
const sourceFields = sourceOutputType?.fields || {};
|
|
508
|
+
if (Object.keys(sourceFields).length === 0) {
|
|
509
|
+
console.warn("No source fields found for", sourceNode, sourceOperation);
|
|
510
|
+
nodes.push({
|
|
511
|
+
id: `source-placeholder`,
|
|
512
|
+
position: {
|
|
513
|
+
x: 100,
|
|
514
|
+
y: 70
|
|
515
|
+
},
|
|
516
|
+
data: {
|
|
517
|
+
label: "No fields available",
|
|
518
|
+
type: "String",
|
|
519
|
+
path: "placeholder",
|
|
520
|
+
side: "source",
|
|
521
|
+
isRequired: false,
|
|
522
|
+
isNested: false,
|
|
523
|
+
isArray: false,
|
|
524
|
+
nodeType: sourceNode
|
|
525
|
+
},
|
|
526
|
+
type: "schemaNode",
|
|
527
|
+
parentId: "source-group",
|
|
528
|
+
extent: "parent"
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
const generateFieldNodes = (fields, parentPath = "", xPos, startYPos, side, parentNodeType, schema) => {
|
|
532
|
+
let currentYPos = startYPos;
|
|
533
|
+
const generatedNodes = [];
|
|
534
|
+
const xOffset = 100;
|
|
535
|
+
const categorizedFields = {
|
|
536
|
+
input: {},
|
|
537
|
+
metadata: {},
|
|
538
|
+
context: {},
|
|
539
|
+
output: {},
|
|
540
|
+
config: {},
|
|
541
|
+
other: {}
|
|
542
|
+
};
|
|
543
|
+
for (const fieldName in fields) {
|
|
544
|
+
const field = fields[fieldName];
|
|
545
|
+
if (fieldName.toLowerCase().includes("input") || fieldName.toLowerCase().includes("request")) categorizedFields.input[fieldName] = field;
|
|
546
|
+
else if (fieldName.toLowerCase().includes("metadata") || fieldName.toLowerCase().includes("meta")) categorizedFields.metadata[fieldName] = field;
|
|
547
|
+
else if (fieldName.toLowerCase().includes("context") || fieldName.toLowerCase().includes("ctx")) categorizedFields.context[fieldName] = field;
|
|
548
|
+
else if (fieldName.toLowerCase().includes("output") || fieldName.toLowerCase().includes("response") || fieldName.toLowerCase().includes("result")) categorizedFields.output[fieldName] = field;
|
|
549
|
+
else if (fieldName.toLowerCase().includes("config") || fieldName.toLowerCase().includes("setting") || fieldName.toLowerCase().includes("option")) categorizedFields.config[fieldName] = field;
|
|
550
|
+
else categorizedFields.other[fieldName] = field;
|
|
551
|
+
}
|
|
552
|
+
const categories = Object.keys(categorizedFields);
|
|
553
|
+
for (const category of categories) {
|
|
554
|
+
const categoryFields = categorizedFields[category];
|
|
555
|
+
if (Object.keys(categoryFields).length === 0) continue;
|
|
556
|
+
if (generatedNodes.length > 0) currentYPos += 20;
|
|
557
|
+
for (const fieldName in categoryFields) {
|
|
558
|
+
const field = categoryFields[fieldName];
|
|
559
|
+
const fullPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
|
|
560
|
+
const node = {
|
|
561
|
+
id: `${side}-${fullPath}`,
|
|
562
|
+
position: {
|
|
563
|
+
x: xOffset,
|
|
564
|
+
y: currentYPos
|
|
565
|
+
},
|
|
566
|
+
data: {
|
|
567
|
+
label: fieldName,
|
|
568
|
+
type: field.type,
|
|
569
|
+
path: fullPath,
|
|
570
|
+
side,
|
|
571
|
+
isRequired: field.isNonNull,
|
|
572
|
+
isNested: isComplexType(schema, field.type),
|
|
573
|
+
isArray: field.isList,
|
|
574
|
+
nodeType: parentNodeType,
|
|
575
|
+
category: category !== "other" ? category : void 0
|
|
576
|
+
},
|
|
577
|
+
type: "schemaNode",
|
|
578
|
+
parentId: side === "source" ? "source-group" : "target-group",
|
|
579
|
+
extent: "parent"
|
|
580
|
+
};
|
|
581
|
+
generatedNodes.push(node);
|
|
582
|
+
currentYPos += 70;
|
|
583
|
+
if (isComplexType(schema, field.type) && schema.types[field.type]) {
|
|
584
|
+
const nestedFields = schema.types[field.type].fields || {};
|
|
585
|
+
if (Object.keys(nestedFields).length > 0) {
|
|
586
|
+
const nestedNodes = generateFieldNodes(nestedFields, fullPath, xPos + (side === "source" ? 150 : -150), currentYPos - 20, side, parentNodeType, schema);
|
|
587
|
+
generatedNodes.push(...nestedNodes);
|
|
588
|
+
currentYPos += nestedNodes.length * 60 + 30;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return generatedNodes;
|
|
594
|
+
};
|
|
595
|
+
const sourceNodes = generateFieldNodes(sourceFields, "", 100, yPos, "source", sourceNode, sourceSchema);
|
|
596
|
+
nodes.push(...sourceNodes);
|
|
597
|
+
const targetFields = {};
|
|
598
|
+
if (useArgsDirectlyForTarget && targetOperationField?.args) for (const arg of targetOperationField.args) targetFields[arg.name] = {
|
|
599
|
+
name: arg.name,
|
|
600
|
+
type: arg.type,
|
|
601
|
+
isNonNull: arg.isNonNull || false,
|
|
602
|
+
isList: arg.isList || false,
|
|
603
|
+
args: []
|
|
604
|
+
};
|
|
605
|
+
else if (targetInputType) {
|
|
606
|
+
const inputFieldsSource = targetInputType.inputFields || targetInputType.fields || {};
|
|
607
|
+
for (const fieldName of Object.keys(inputFieldsSource)) {
|
|
608
|
+
const field = inputFieldsSource[fieldName];
|
|
609
|
+
targetFields[fieldName] = {
|
|
610
|
+
name: field.name,
|
|
611
|
+
type: field.type,
|
|
612
|
+
isNonNull: field.isNonNull,
|
|
613
|
+
isList: field.isList,
|
|
614
|
+
args: field.args || []
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (Object.keys(targetFields).length === 0) {
|
|
619
|
+
const processOperationArgs = (operationField) => {
|
|
620
|
+
if (!operationField?.args || operationField.args.length === 0) return false;
|
|
621
|
+
const inputArg = operationField.args.find((a) => a.name === "input" || a.name === "state");
|
|
622
|
+
if (inputArg) {
|
|
623
|
+
const inputType = targetSchema.types[inputArg.type];
|
|
624
|
+
if (inputType && inputType.kind === "input" && inputType.inputFields) {
|
|
625
|
+
for (const fieldName of Object.keys(inputType.inputFields)) {
|
|
626
|
+
const field = inputType.inputFields[fieldName];
|
|
627
|
+
targetFields[fieldName] = {
|
|
628
|
+
name: field.name || fieldName,
|
|
629
|
+
type: field.type,
|
|
630
|
+
isNonNull: field.isNonNull || false,
|
|
631
|
+
isList: field.isList || false,
|
|
632
|
+
args: []
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (operationField.args.length > 0) {
|
|
639
|
+
const firstArg = operationField.args[0];
|
|
640
|
+
const firstArgType = targetSchema.types[firstArg.type];
|
|
641
|
+
if (firstArgType && firstArgType.kind === "input" && firstArgType.inputFields) {
|
|
642
|
+
for (const fieldName of Object.keys(firstArgType.inputFields)) {
|
|
643
|
+
const field = firstArgType.inputFields[fieldName];
|
|
644
|
+
targetFields[fieldName] = {
|
|
645
|
+
name: field.name || fieldName,
|
|
646
|
+
type: field.type,
|
|
647
|
+
isNonNull: field.isNonNull || false,
|
|
648
|
+
isList: field.isList || false,
|
|
649
|
+
args: []
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
for (const arg of operationField.args) targetFields[arg.name] = {
|
|
656
|
+
name: arg.name,
|
|
657
|
+
type: arg.type,
|
|
658
|
+
isNonNull: arg.isNonNull || false,
|
|
659
|
+
isList: arg.isList || false,
|
|
660
|
+
args: []
|
|
661
|
+
};
|
|
662
|
+
return true;
|
|
663
|
+
};
|
|
664
|
+
if (targetSchema.mutationType && targetSchema.types[targetSchema.mutationType]) {
|
|
665
|
+
const mutationField = targetSchema.types[targetSchema.mutationType].fields?.[targetOperation];
|
|
666
|
+
if (mutationField) processOperationArgs(mutationField);
|
|
667
|
+
}
|
|
668
|
+
if (Object.keys(targetFields).length === 0 && targetSchema.queryType && targetSchema.types[targetSchema.queryType]) {
|
|
669
|
+
const queryField = targetSchema.types[targetSchema.queryType].fields?.[targetOperation];
|
|
670
|
+
if (queryField) processOperationArgs(queryField);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (Object.keys(targetFields).length === 0) {
|
|
674
|
+
console.warn("No target fields found for", targetNode, targetOperation);
|
|
675
|
+
nodes.push({
|
|
676
|
+
id: `target-placeholder`,
|
|
677
|
+
position: {
|
|
678
|
+
x: 100,
|
|
679
|
+
y: 70
|
|
680
|
+
},
|
|
681
|
+
data: {
|
|
682
|
+
label: "No fields available",
|
|
683
|
+
type: "String",
|
|
684
|
+
path: "placeholder",
|
|
685
|
+
side: "target",
|
|
686
|
+
isRequired: false,
|
|
687
|
+
isNested: false,
|
|
688
|
+
isArray: false,
|
|
689
|
+
nodeType: targetNode
|
|
690
|
+
},
|
|
691
|
+
type: "schemaNode",
|
|
692
|
+
parentId: "target-group",
|
|
693
|
+
extent: "parent"
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
const targetNodes = generateFieldNodes(targetFields, "", 100, yPos, "target", targetNode, targetSchema);
|
|
697
|
+
nodes.push(...targetNodes);
|
|
698
|
+
if (sourceNodes.length > 0) {
|
|
699
|
+
const maxSourceY = Math.max(...sourceNodes.map((node) => {
|
|
700
|
+
return node.position.y;
|
|
701
|
+
})) + 150;
|
|
702
|
+
if (sourceGroupNode.style) sourceGroupNode.style.height = Math.max(maxSourceY, 600);
|
|
703
|
+
const maxSourceX = Math.max(...sourceNodes.map((node) => node.position.x + 300));
|
|
704
|
+
if (sourceGroupNode.style) sourceGroupNode.style.width = Math.max(maxSourceX, 400);
|
|
705
|
+
}
|
|
706
|
+
if (targetNodes.length > 0) {
|
|
707
|
+
const maxTargetY = Math.max(...targetNodes.map((node) => {
|
|
708
|
+
return node.position.y;
|
|
709
|
+
})) + 150;
|
|
710
|
+
if (targetGroupNode.style) targetGroupNode.style.height = Math.max(maxTargetY, 600);
|
|
711
|
+
const maxTargetX = Math.max(...targetNodes.map((node) => node.position.x + 300));
|
|
712
|
+
if (targetGroupNode.style) targetGroupNode.style.width = Math.max(maxTargetX, 400);
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
nodes,
|
|
716
|
+
edges
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function isComplexType(schema, typeName) {
|
|
720
|
+
const type = schema.types[typeName];
|
|
721
|
+
if (!type) return false;
|
|
722
|
+
return type.kind === "type" || type.kind === "input";
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// src/builder/components/hooks/use-toast.ts
|
|
726
|
+
const TOAST_LIMIT = 5;
|
|
727
|
+
const TOAST_REMOVE_DELAY = 1e6;
|
|
728
|
+
const actionTypes = {
|
|
729
|
+
ADD_TOAST: "ADD_TOAST",
|
|
730
|
+
UPDATE_TOAST: "UPDATE_TOAST",
|
|
731
|
+
DISMISS_TOAST: "DISMISS_TOAST",
|
|
732
|
+
REMOVE_TOAST: "REMOVE_TOAST"
|
|
733
|
+
};
|
|
734
|
+
let count = 0;
|
|
735
|
+
function genId() {
|
|
736
|
+
count = (count + 1) % Number.MAX_VALUE;
|
|
737
|
+
return count.toString();
|
|
738
|
+
}
|
|
739
|
+
const toastTimeouts = /* @__PURE__ */ new Map();
|
|
740
|
+
const reducer = (state, action) => {
|
|
741
|
+
switch (action.type) {
|
|
742
|
+
case actionTypes.ADD_TOAST: return {
|
|
743
|
+
...state,
|
|
744
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
|
745
|
+
};
|
|
746
|
+
case actionTypes.UPDATE_TOAST: return {
|
|
747
|
+
...state,
|
|
748
|
+
toasts: state.toasts.map((t) => t.id === action.toast.id ? {
|
|
749
|
+
...t,
|
|
750
|
+
...action.toast
|
|
751
|
+
} : t)
|
|
752
|
+
};
|
|
753
|
+
case actionTypes.DISMISS_TOAST: {
|
|
754
|
+
const { toastId } = action;
|
|
755
|
+
if (toastId) addToRemoveQueue(toastId);
|
|
756
|
+
else state.toasts.forEach((toast) => {
|
|
757
|
+
addToRemoveQueue(toast.id);
|
|
758
|
+
});
|
|
759
|
+
return {
|
|
760
|
+
...state,
|
|
761
|
+
toasts: state.toasts.map((t) => t.id === toastId || toastId === void 0 ? {
|
|
762
|
+
...t,
|
|
763
|
+
open: false
|
|
764
|
+
} : t)
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
case actionTypes.REMOVE_TOAST:
|
|
768
|
+
if (action.toastId === void 0) return {
|
|
769
|
+
...state,
|
|
770
|
+
toasts: []
|
|
771
|
+
};
|
|
772
|
+
return {
|
|
773
|
+
...state,
|
|
774
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId)
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
const listeners = [];
|
|
779
|
+
let memoryState = { toasts: [] };
|
|
780
|
+
function dispatch(action) {
|
|
781
|
+
memoryState = reducer(memoryState, action);
|
|
782
|
+
listeners.forEach((listener) => {
|
|
783
|
+
listener(memoryState);
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
function toast({ ...props }) {
|
|
787
|
+
const id = genId();
|
|
788
|
+
const update = (props) => dispatch({
|
|
789
|
+
type: actionTypes.UPDATE_TOAST,
|
|
790
|
+
toast: {
|
|
791
|
+
...props,
|
|
792
|
+
id
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
const dismiss = () => dispatch({
|
|
796
|
+
type: actionTypes.DISMISS_TOAST,
|
|
797
|
+
toastId: id
|
|
798
|
+
});
|
|
799
|
+
dispatch({
|
|
800
|
+
type: actionTypes.ADD_TOAST,
|
|
801
|
+
toast: {
|
|
802
|
+
...props,
|
|
803
|
+
id,
|
|
804
|
+
open: true,
|
|
805
|
+
onOpenChange: (open) => {
|
|
806
|
+
if (!open) dismiss();
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
return {
|
|
811
|
+
id,
|
|
812
|
+
dismiss,
|
|
813
|
+
update
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function useToast() {
|
|
817
|
+
const [state, setState] = React$1.useState(memoryState);
|
|
818
|
+
React$1.useEffect(() => {
|
|
819
|
+
listeners.push(setState);
|
|
820
|
+
return () => {
|
|
821
|
+
const index = listeners.indexOf(setState);
|
|
822
|
+
if (index > -1) listeners.splice(index, 1);
|
|
823
|
+
};
|
|
824
|
+
}, [state]);
|
|
825
|
+
return {
|
|
826
|
+
...state,
|
|
827
|
+
toast,
|
|
828
|
+
dismiss: (toastId) => dispatch({
|
|
829
|
+
type: actionTypes.DISMISS_TOAST,
|
|
830
|
+
toastId
|
|
831
|
+
})
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function addToRemoveQueue(toastId) {
|
|
835
|
+
if (toastTimeouts.has(toastId)) return;
|
|
836
|
+
const timeout = setTimeout(() => {
|
|
837
|
+
toastTimeouts.delete(toastId);
|
|
838
|
+
dispatch({
|
|
839
|
+
type: actionTypes.REMOVE_TOAST,
|
|
840
|
+
toastId
|
|
841
|
+
});
|
|
842
|
+
}, TOAST_REMOVE_DELAY);
|
|
843
|
+
toastTimeouts.set(toastId, timeout);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/builder/components/mapper/config/data-config-context.tsx
|
|
847
|
+
const DataConfigContext = createContext(null);
|
|
848
|
+
function DataConfigProvider({ children, config }) {
|
|
849
|
+
return /* @__PURE__ */ jsx(DataConfigContext.Provider, {
|
|
850
|
+
value: config,
|
|
851
|
+
children
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
function useDataConfig() {
|
|
855
|
+
const context = useContext(DataConfigContext);
|
|
856
|
+
if (!context) {
|
|
857
|
+
console.warn("useDataConfig: No DataConfigProvider found, using empty config");
|
|
858
|
+
return {
|
|
859
|
+
nodes: {},
|
|
860
|
+
mappingTypes: {},
|
|
861
|
+
typeCompatibility: {},
|
|
862
|
+
sampleData: {}
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
return context;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/builder/components/mapper/mapping-dialog.tsx
|
|
869
|
+
function MappingDialog(props) {
|
|
870
|
+
if (!props.open) return null;
|
|
871
|
+
const dialogKey = `${props.mappingType}-${props.sourceField || ""}-${props.targetField || ""}`;
|
|
872
|
+
return /* @__PURE__ */ jsx(MappingDialogContent, { ...props }, dialogKey);
|
|
873
|
+
}
|
|
874
|
+
function MappingDialogContent({ open, onOpenChange, onSave, mappingType, sourceField, targetField, sourceSchema, sourceOperation, targetSchema, targetOperation }) {
|
|
875
|
+
const [expression, setExpression] = useState(() => sourceField ? `\${output.${sourceField}}` : "");
|
|
876
|
+
const [selectedExample, setSelectedExample] = useState("");
|
|
877
|
+
const [activeTab, setActiveTab] = useState(() => mappingType === "array" || mappingType === "object" ? "builder" : "examples");
|
|
878
|
+
const [targetFieldName, setTargetFieldName] = useState(() => targetField || "");
|
|
879
|
+
const [selectedSourceArray, setSelectedSourceArray] = useState("");
|
|
880
|
+
const [itemExpression, setItemExpression] = useState("");
|
|
881
|
+
const dataConfig = useDataConfig();
|
|
882
|
+
const tr = useTr();
|
|
883
|
+
const sourceFields = useMemo(() => {
|
|
884
|
+
if (!sourceSchema) return [];
|
|
885
|
+
const fields = [];
|
|
886
|
+
const extractFields = (typeName, parentPath = "", depth = 0) => {
|
|
887
|
+
if (depth > 3) return [];
|
|
888
|
+
const typeInfo = sourceSchema.types[typeName];
|
|
889
|
+
if (!typeInfo || !typeInfo.fields) return [];
|
|
890
|
+
const result = [];
|
|
891
|
+
for (const fieldName in typeInfo.fields) {
|
|
892
|
+
const field = typeInfo.fields[fieldName];
|
|
893
|
+
const fieldPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
|
|
894
|
+
const fieldInfo = {
|
|
895
|
+
path: fieldPath,
|
|
896
|
+
name: fieldName,
|
|
897
|
+
type: field.type,
|
|
898
|
+
isList: field.isList || false,
|
|
899
|
+
isNonNull: field.isNonNull || false
|
|
900
|
+
};
|
|
901
|
+
if (sourceSchema.types[field.type] && sourceSchema.types[field.type].kind === "type") fieldInfo.children = extractFields(field.type, fieldPath, depth + 1);
|
|
902
|
+
result.push(fieldInfo);
|
|
903
|
+
}
|
|
904
|
+
return result;
|
|
905
|
+
};
|
|
906
|
+
const queryType = sourceSchema.types[sourceSchema.queryType || "Query"];
|
|
907
|
+
if (queryType && queryType.fields && sourceOperation) {
|
|
908
|
+
const operationField = queryType.fields[sourceOperation];
|
|
909
|
+
if (operationField) {
|
|
910
|
+
const returnType = operationField.type;
|
|
911
|
+
if (sourceSchema.types[returnType]) return extractFields(returnType, sourceOperation);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
for (const typeName in sourceSchema.types) if (sourceSchema.types[typeName].kind === "type" && !typeName.startsWith("_") && typeName !== "Query" && typeName !== "Mutation") fields.push(...extractFields(typeName, ""));
|
|
915
|
+
return fields;
|
|
916
|
+
}, [sourceSchema, sourceOperation]);
|
|
917
|
+
const arrayFields = useMemo(() => {
|
|
918
|
+
const arrays = [];
|
|
919
|
+
const collectArrays = (fields, prefix = "") => {
|
|
920
|
+
for (const field of fields) {
|
|
921
|
+
if (field.isList) arrays.push({
|
|
922
|
+
...field,
|
|
923
|
+
path: prefix ? `${prefix}.${field.path}` : field.path
|
|
924
|
+
});
|
|
925
|
+
if (field.children) collectArrays(field.children, prefix);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
collectArrays(sourceFields);
|
|
929
|
+
return arrays;
|
|
930
|
+
}, [sourceFields]);
|
|
931
|
+
const arrayItemFields = useMemo(() => {
|
|
932
|
+
if (!selectedSourceArray || !sourceSchema) return [];
|
|
933
|
+
const findField = (fields, path) => {
|
|
934
|
+
for (const field of fields) {
|
|
935
|
+
if (field.path === path) return field;
|
|
936
|
+
if (field.children) {
|
|
937
|
+
const found = findField(field.children, path);
|
|
938
|
+
if (found) return found;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return null;
|
|
942
|
+
};
|
|
943
|
+
const arrayField = findField(sourceFields, selectedSourceArray);
|
|
944
|
+
if (!arrayField) return [];
|
|
945
|
+
const itemType = sourceSchema.types[arrayField.type];
|
|
946
|
+
if (!itemType || !itemType.fields) return [];
|
|
947
|
+
const result = [];
|
|
948
|
+
const extractNestedFields = (typeName, parentPath = "", currentDepth = 0) => {
|
|
949
|
+
if (currentDepth > 2) return;
|
|
950
|
+
const type = sourceSchema?.types[typeName];
|
|
951
|
+
if (!type || !type.fields) return;
|
|
952
|
+
for (const fieldName in type.fields) {
|
|
953
|
+
const field = type.fields[fieldName];
|
|
954
|
+
const fullPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
|
|
955
|
+
result.push({
|
|
956
|
+
name: fieldName,
|
|
957
|
+
type: field.type,
|
|
958
|
+
path: fullPath,
|
|
959
|
+
isNested: currentDepth > 0
|
|
960
|
+
});
|
|
961
|
+
const fieldType = sourceSchema?.types[field.type];
|
|
962
|
+
if (fieldType && fieldType.kind === "type" && !field.isList) extractNestedFields(field.type, fullPath, currentDepth + 1);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
extractNestedFields(arrayField.type);
|
|
966
|
+
return result;
|
|
967
|
+
}, [
|
|
968
|
+
selectedSourceArray,
|
|
969
|
+
sourceFields,
|
|
970
|
+
sourceSchema
|
|
971
|
+
]);
|
|
972
|
+
const targetFields = useMemo(() => {
|
|
973
|
+
if (!targetSchema) return [];
|
|
974
|
+
const fields = [];
|
|
975
|
+
const extractFields = (typeName, parentPath = "", depth = 0) => {
|
|
976
|
+
if (depth > 3) return [];
|
|
977
|
+
const typeInfo = targetSchema.types[typeName];
|
|
978
|
+
if (!typeInfo) return [];
|
|
979
|
+
const fieldsObj = typeInfo.inputFields || typeInfo.fields;
|
|
980
|
+
if (!fieldsObj) return [];
|
|
981
|
+
const result = [];
|
|
982
|
+
for (const fieldName in fieldsObj) {
|
|
983
|
+
const field = fieldsObj[fieldName];
|
|
984
|
+
const fieldPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
|
|
985
|
+
const fieldInfo = {
|
|
986
|
+
path: fieldPath,
|
|
987
|
+
name: fieldName,
|
|
988
|
+
type: field.type,
|
|
989
|
+
isList: field.isList || false,
|
|
990
|
+
isNonNull: field.isNonNull || false
|
|
991
|
+
};
|
|
992
|
+
if (targetSchema.types[field.type]) {
|
|
993
|
+
const nestedType = targetSchema.types[field.type];
|
|
994
|
+
if (nestedType.kind === "type" || nestedType.kind === "input") fieldInfo.children = extractFields(field.type, fieldPath, depth + 1);
|
|
995
|
+
}
|
|
996
|
+
result.push(fieldInfo);
|
|
997
|
+
}
|
|
998
|
+
return result;
|
|
999
|
+
};
|
|
1000
|
+
const mutationType = targetSchema.types[targetSchema.mutationType || "Mutation"];
|
|
1001
|
+
const queryType = targetSchema.types[targetSchema.queryType || "Query"];
|
|
1002
|
+
if (targetOperation) {
|
|
1003
|
+
if (mutationType?.fields?.[targetOperation]) {
|
|
1004
|
+
const operationField = mutationType.fields[targetOperation];
|
|
1005
|
+
if (operationField.args && operationField.args.length > 0) for (const arg of operationField.args) if (targetSchema.types[arg.type]) fields.push(...extractFields(arg.type, ""));
|
|
1006
|
+
else fields.push({
|
|
1007
|
+
path: arg.name,
|
|
1008
|
+
name: arg.name,
|
|
1009
|
+
type: arg.type,
|
|
1010
|
+
isList: arg.isList || false,
|
|
1011
|
+
isNonNull: arg.isNonNull || false
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
if (queryType?.fields?.[targetOperation]) {
|
|
1015
|
+
const operationField = queryType.fields[targetOperation];
|
|
1016
|
+
if (operationField.args && operationField.args.length > 0) for (const arg of operationField.args) if (targetSchema.types[arg.type]) fields.push(...extractFields(arg.type, ""));
|
|
1017
|
+
else fields.push({
|
|
1018
|
+
path: arg.name,
|
|
1019
|
+
name: arg.name,
|
|
1020
|
+
type: arg.type,
|
|
1021
|
+
isList: arg.isList || false,
|
|
1022
|
+
isNonNull: arg.isNonNull || false
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return fields;
|
|
1027
|
+
}, [targetSchema, targetOperation]);
|
|
1028
|
+
const handleSourceArrayChange = (value) => {
|
|
1029
|
+
setSelectedSourceArray(value);
|
|
1030
|
+
if (mappingType === "array" && value && itemExpression) setExpression(`\${output.${value}.map(item => item.${itemExpression})}`);
|
|
1031
|
+
};
|
|
1032
|
+
const handleItemExpressionChange = (value) => {
|
|
1033
|
+
setItemExpression(value);
|
|
1034
|
+
if (mappingType === "array" && selectedSourceArray && value) setExpression(`\${output.${selectedSourceArray}.map(item => item.${value})}`);
|
|
1035
|
+
};
|
|
1036
|
+
const getMappingTypeConfig = () => {
|
|
1037
|
+
return dataConfig?.mappingTypes?.[mappingType] || {};
|
|
1038
|
+
};
|
|
1039
|
+
const mappingTypeConfig = getMappingTypeConfig();
|
|
1040
|
+
const examples = mappingTypeConfig.examples || [];
|
|
1041
|
+
const safeBuiltins = mappingTypeConfig.safeBuiltins || [];
|
|
1042
|
+
const isFormValid = useMemo(() => {
|
|
1043
|
+
if (!targetFieldName.trim()) return false;
|
|
1044
|
+
if (!expression.trim()) return false;
|
|
1045
|
+
return true;
|
|
1046
|
+
}, [targetFieldName, expression]);
|
|
1047
|
+
const getValidationMessage = () => {
|
|
1048
|
+
const missing = [];
|
|
1049
|
+
if (!targetFieldName.trim()) missing.push(tr("fluidgrids.builder.mappingDialog.missingTargetFieldName", "target field name"));
|
|
1050
|
+
if (!expression.trim()) missing.push(tr("fluidgrids.builder.mappingDialog.missingExpression", "expression"));
|
|
1051
|
+
if (missing.length === 0) return null;
|
|
1052
|
+
return `${tr("fluidgrids.builder.mappingDialog.validationPrefix", "Please provide:")} ${missing.join(", ")}`;
|
|
1053
|
+
};
|
|
1054
|
+
const handleExampleChange = (exampleName) => {
|
|
1055
|
+
setSelectedExample(exampleName);
|
|
1056
|
+
if (exampleName && exampleName !== "choose") {
|
|
1057
|
+
const example = examples.find((ex) => ex.name === exampleName);
|
|
1058
|
+
if (example && example.expression) setExpression(example.expression);
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
const handleSave = () => {
|
|
1062
|
+
onSave(expression, targetFieldName || void 0);
|
|
1063
|
+
onOpenChange(false);
|
|
1064
|
+
};
|
|
1065
|
+
const getDialogTitle = () => {
|
|
1066
|
+
switch (mappingType) {
|
|
1067
|
+
case "direct": return tr("fluidgrids.builder.mappingDialog.titleDirect", "Direct Field Mapping");
|
|
1068
|
+
case "expression": return tr("fluidgrids.builder.mappingDialog.titleExpression", "Expression Mapping");
|
|
1069
|
+
case "transform": return tr("fluidgrids.builder.mappingDialog.titleTransform", "Transform Mapping");
|
|
1070
|
+
case "cast": return tr("fluidgrids.builder.mappingDialog.titleCast", "Type Cast Mapping");
|
|
1071
|
+
case "array": return tr("fluidgrids.builder.mappingDialog.titleArray", "Array Mapping");
|
|
1072
|
+
case "object": return tr("fluidgrids.builder.mappingDialog.titleObject", "Object Mapping");
|
|
1073
|
+
case "context": return tr("fluidgrids.builder.mappingDialog.titleContext", "Workflow Context");
|
|
1074
|
+
default: return tr("fluidgrids.builder.mappingDialog.titleDefault", "Data Mapping");
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
const getDialogDescription = () => {
|
|
1078
|
+
return mappingTypeConfig.description || tr("fluidgrids.builder.mappingDialog.defaultDescription", "Create a mapping using JavaScript template literal syntax");
|
|
1079
|
+
};
|
|
1080
|
+
const getPlaceholder = () => {
|
|
1081
|
+
switch (mappingType) {
|
|
1082
|
+
case "direct": return "${output.fieldName}";
|
|
1083
|
+
case "expression": return "${output.firstName + ' ' + output.lastName}";
|
|
1084
|
+
case "transform": return "${output.field.toUpperCase()}";
|
|
1085
|
+
case "cast": return "${parseInt(output.numberString)}";
|
|
1086
|
+
case "array": return "${output.items.map(item => item.id)}";
|
|
1087
|
+
case "object": return "${({ id: output.user.id, email: output.user.email })}";
|
|
1088
|
+
case "context": return "${TriggerInput.userId}";
|
|
1089
|
+
default: return "${output.field}";
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
const getQuickInserts = () => {
|
|
1093
|
+
switch (mappingType) {
|
|
1094
|
+
case "direct": return [
|
|
1095
|
+
{
|
|
1096
|
+
label: "output.",
|
|
1097
|
+
snippet: "output."
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
label: "?.",
|
|
1101
|
+
snippet: "?."
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
label: "|| ''",
|
|
1105
|
+
snippet: " || ''"
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
label: "?? null",
|
|
1109
|
+
snippet: " ?? null"
|
|
1110
|
+
}
|
|
1111
|
+
];
|
|
1112
|
+
case "expression": return [
|
|
1113
|
+
{
|
|
1114
|
+
label: "output.",
|
|
1115
|
+
snippet: "output."
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
label: "? :",
|
|
1119
|
+
snippet: " ? '' : ''"
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
label: "&&",
|
|
1123
|
+
snippet: " && "
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
label: "||",
|
|
1127
|
+
snippet: " || "
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
label: "??",
|
|
1131
|
+
snippet: " ?? "
|
|
1132
|
+
}
|
|
1133
|
+
];
|
|
1134
|
+
case "transform": return [
|
|
1135
|
+
{
|
|
1136
|
+
label: ".toUpperCase()",
|
|
1137
|
+
snippet: ".toUpperCase()"
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
label: ".toLowerCase()",
|
|
1141
|
+
snippet: ".toLowerCase()"
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
label: ".trim()",
|
|
1145
|
+
snippet: ".trim()"
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
label: ".split(',')",
|
|
1149
|
+
snippet: ".split(',')"
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
label: ".join(', ')",
|
|
1153
|
+
snippet: ".join(', ')"
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
label: ".replace()",
|
|
1157
|
+
snippet: ".replace('old', 'new')"
|
|
1158
|
+
}
|
|
1159
|
+
];
|
|
1160
|
+
case "cast": return [
|
|
1161
|
+
{
|
|
1162
|
+
label: "parseInt()",
|
|
1163
|
+
snippet: "parseInt("
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
label: "parseFloat()",
|
|
1167
|
+
snippet: "parseFloat("
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
label: "String()",
|
|
1171
|
+
snippet: "String("
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
label: "Boolean()",
|
|
1175
|
+
snippet: "Boolean("
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
label: "Number()",
|
|
1179
|
+
snippet: "Number("
|
|
1180
|
+
}
|
|
1181
|
+
];
|
|
1182
|
+
case "array": return [
|
|
1183
|
+
{
|
|
1184
|
+
label: ".map()",
|
|
1185
|
+
snippet: ".map(item => item)"
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
label: ".filter()",
|
|
1189
|
+
snippet: ".filter(item => item)"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
label: ".reduce()",
|
|
1193
|
+
snippet: ".reduce((acc, item) => acc, 0)"
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
label: ".find()",
|
|
1197
|
+
snippet: ".find(item => item)"
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
label: ".some()",
|
|
1201
|
+
snippet: ".some(item => item)"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
label: ".every()",
|
|
1205
|
+
snippet: ".every(item => item)"
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
label: ".flat()",
|
|
1209
|
+
snippet: ".flat()"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
label: ".slice()",
|
|
1213
|
+
snippet: ".slice(0, 5)"
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
label: ".sort()",
|
|
1217
|
+
snippet: ".sort((a, b) => a - b)"
|
|
1218
|
+
}
|
|
1219
|
+
];
|
|
1220
|
+
case "object": return [
|
|
1221
|
+
{
|
|
1222
|
+
label: "Object.keys()",
|
|
1223
|
+
snippet: "Object.keys("
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
label: "Object.values()",
|
|
1227
|
+
snippet: "Object.values("
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
label: "Object.entries()",
|
|
1231
|
+
snippet: "Object.entries("
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
label: "Object.assign()",
|
|
1235
|
+
snippet: "Object.assign({}, "
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
label: "{ ...obj }",
|
|
1239
|
+
snippet: "{ ..."
|
|
1240
|
+
}
|
|
1241
|
+
];
|
|
1242
|
+
case "context": return [
|
|
1243
|
+
{
|
|
1244
|
+
label: "TriggerInput.",
|
|
1245
|
+
snippet: "TriggerInput."
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
label: "WorkflowState.",
|
|
1249
|
+
snippet: "WorkflowState."
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
label: "RunContext.",
|
|
1253
|
+
snippet: "RunContext."
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
label: ".output.",
|
|
1257
|
+
snippet: ".output."
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
label: ".status",
|
|
1261
|
+
snippet: ".status"
|
|
1262
|
+
}
|
|
1263
|
+
];
|
|
1264
|
+
default: return [];
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
const insertSnippet = (snippet) => {
|
|
1268
|
+
const textarea = document.getElementById("expression-input");
|
|
1269
|
+
if (textarea) {
|
|
1270
|
+
const start = textarea.selectionStart;
|
|
1271
|
+
const end = textarea.selectionEnd;
|
|
1272
|
+
const newValue = expression.slice(0, start) + snippet + expression.slice(end);
|
|
1273
|
+
setExpression(newValue);
|
|
1274
|
+
setTimeout(() => {
|
|
1275
|
+
textarea.focus();
|
|
1276
|
+
textarea.setSelectionRange(start + snippet.length, start + snippet.length);
|
|
1277
|
+
}, 0);
|
|
1278
|
+
} else setExpression(expression + snippet);
|
|
1279
|
+
};
|
|
1280
|
+
if (!open) return null;
|
|
1281
|
+
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
1282
|
+
className: "fixed inset-0 bg-bg-overlay z-[100001] flex items-center justify-center",
|
|
1283
|
+
onClick: () => onOpenChange(false),
|
|
1284
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1285
|
+
className: "bg-surface dark:bg-surface z-[100002] flex flex-col w-[700px] max-w-[95vw] max-h-[90vh] rounded-lg overflow-hidden pointer-events-auto shadow-xl",
|
|
1286
|
+
onClick: (e) => e.stopPropagation(),
|
|
1287
|
+
children: [
|
|
1288
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1289
|
+
className: "flex justify-between items-start p-6 border-b",
|
|
1290
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h2", {
|
|
1291
|
+
className: "text-lg font-semibold",
|
|
1292
|
+
children: getDialogTitle()
|
|
1293
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
1294
|
+
className: "text-sm text-muted-foreground mt-1",
|
|
1295
|
+
children: getDialogDescription()
|
|
1296
|
+
})] }), /* @__PURE__ */ jsx("button", {
|
|
1297
|
+
onClick: () => onOpenChange(false),
|
|
1298
|
+
className: "rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
1299
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
1300
|
+
})]
|
|
1301
|
+
}),
|
|
1302
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1303
|
+
className: "flex-1 overflow-y-auto p-6",
|
|
1304
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1305
|
+
className: "mb-4 p-3 bg-[var(--color-bg-accent)]/10 rounded-md border border-[var(--color-border-accent)]",
|
|
1306
|
+
children: [/* @__PURE__ */ jsxs(Label, {
|
|
1307
|
+
htmlFor: "target-field-name",
|
|
1308
|
+
className: "text-sm font-medium",
|
|
1309
|
+
children: [
|
|
1310
|
+
tr("fluidgrids.builder.mappingDialog.targetFieldNameLabel", "Target Field Name"),
|
|
1311
|
+
" ",
|
|
1312
|
+
/* @__PURE__ */ jsx("span", {
|
|
1313
|
+
className: "text-muted-foreground",
|
|
1314
|
+
children: tr("fluidgrids.builder.mappingDialog.targetFieldNameHint", "(where the result will be stored)")
|
|
1315
|
+
})
|
|
1316
|
+
]
|
|
1317
|
+
}), targetFields.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
1318
|
+
className: "mt-2 space-y-2",
|
|
1319
|
+
children: [
|
|
1320
|
+
/* @__PURE__ */ jsxs(Select, {
|
|
1321
|
+
value: targetFieldName,
|
|
1322
|
+
onValueChange: setTargetFieldName,
|
|
1323
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: tr("fluidgrids.builder.mappingDialog.selectTargetFieldPlaceholder", "Select target field...") }) }), /* @__PURE__ */ jsx(SelectContent, {
|
|
1324
|
+
className: "z-[100003]",
|
|
1325
|
+
children: targetFields.map((field) => /* @__PURE__ */ jsx(SelectItem, {
|
|
1326
|
+
value: field.path,
|
|
1327
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1328
|
+
className: "flex items-center gap-2",
|
|
1329
|
+
children: [
|
|
1330
|
+
/* @__PURE__ */ jsx("span", {
|
|
1331
|
+
className: "font-mono",
|
|
1332
|
+
children: field.path
|
|
1333
|
+
}),
|
|
1334
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
1335
|
+
variant: "outline",
|
|
1336
|
+
className: "text-xs",
|
|
1337
|
+
children: field.isList ? `[${field.type}]` : field.type
|
|
1338
|
+
}),
|
|
1339
|
+
field.isNonNull && /* @__PURE__ */ jsx("span", {
|
|
1340
|
+
className: "text-[var(--color-status-error-text)] text-xs",
|
|
1341
|
+
children: "*"
|
|
1342
|
+
})
|
|
1343
|
+
]
|
|
1344
|
+
})
|
|
1345
|
+
}, field.path))
|
|
1346
|
+
})]
|
|
1347
|
+
}),
|
|
1348
|
+
/* @__PURE__ */ jsx("p", {
|
|
1349
|
+
className: "text-xs text-muted-foreground",
|
|
1350
|
+
children: tr("fluidgrids.builder.mappingDialog.orCustomFieldName", "Or enter a custom field name:")
|
|
1351
|
+
}),
|
|
1352
|
+
/* @__PURE__ */ jsx(Input, {
|
|
1353
|
+
id: "target-field-name",
|
|
1354
|
+
placeholder: tr("fluidgrids.builder.mappingDialog.targetFieldNamePlaceholder", "e.g., messageIds, userNames, totalPrice"),
|
|
1355
|
+
value: targetFieldName,
|
|
1356
|
+
onChange: (e) => setTargetFieldName(e.target.value),
|
|
1357
|
+
className: "font-mono"
|
|
1358
|
+
})
|
|
1359
|
+
]
|
|
1360
|
+
}) : /* @__PURE__ */ jsx(Input, {
|
|
1361
|
+
id: "target-field-name",
|
|
1362
|
+
placeholder: tr("fluidgrids.builder.mappingDialog.targetFieldNamePlaceholder", "e.g., messageIds, userNames, totalPrice"),
|
|
1363
|
+
value: targetFieldName,
|
|
1364
|
+
onChange: (e) => setTargetFieldName(e.target.value),
|
|
1365
|
+
className: "mt-2"
|
|
1366
|
+
})]
|
|
1367
|
+
}), /* @__PURE__ */ jsxs(Tabs, {
|
|
1368
|
+
value: activeTab,
|
|
1369
|
+
onValueChange: setActiveTab,
|
|
1370
|
+
className: "w-full",
|
|
1371
|
+
children: [
|
|
1372
|
+
/* @__PURE__ */ jsxs(TabsList, {
|
|
1373
|
+
className: "grid w-full grid-cols-4",
|
|
1374
|
+
children: [
|
|
1375
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
1376
|
+
value: "builder",
|
|
1377
|
+
children: tr("fluidgrids.builder.mappingDialog.tabBuilder", "Builder")
|
|
1378
|
+
}),
|
|
1379
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
1380
|
+
value: "examples",
|
|
1381
|
+
children: tr("fluidgrids.builder.mappingDialog.tabExamples", "Examples")
|
|
1382
|
+
}),
|
|
1383
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
1384
|
+
value: "editor",
|
|
1385
|
+
children: tr("fluidgrids.builder.mappingDialog.tabEditor", "Editor")
|
|
1386
|
+
}),
|
|
1387
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
1388
|
+
value: "help",
|
|
1389
|
+
children: tr("fluidgrids.builder.mappingDialog.tabHelp", "Help")
|
|
1390
|
+
})
|
|
1391
|
+
]
|
|
1392
|
+
}),
|
|
1393
|
+
/* @__PURE__ */ jsxs(TabsContent, {
|
|
1394
|
+
value: "builder",
|
|
1395
|
+
className: "space-y-4 mt-4",
|
|
1396
|
+
children: [
|
|
1397
|
+
mappingType === "array" && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1398
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1399
|
+
className: "grid gap-2",
|
|
1400
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.stepSelectSourceArray", "1. Select Source Array") }), arrayFields.length > 0 ? /* @__PURE__ */ jsxs(Select, {
|
|
1401
|
+
value: selectedSourceArray,
|
|
1402
|
+
onValueChange: handleSourceArrayChange,
|
|
1403
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: tr("fluidgrids.builder.mappingDialog.chooseArrayFieldPlaceholder", "Choose an array field...") }) }), /* @__PURE__ */ jsx(SelectContent, {
|
|
1404
|
+
className: "z-[100003]",
|
|
1405
|
+
children: arrayFields.map((field) => /* @__PURE__ */ jsx(SelectItem, {
|
|
1406
|
+
value: field.path,
|
|
1407
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1408
|
+
className: "flex items-center gap-2",
|
|
1409
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1410
|
+
className: "font-mono",
|
|
1411
|
+
children: field.path
|
|
1412
|
+
}), /* @__PURE__ */ jsxs(Badge, {
|
|
1413
|
+
variant: "outline",
|
|
1414
|
+
className: "text-xs",
|
|
1415
|
+
children: [
|
|
1416
|
+
"[",
|
|
1417
|
+
field.type,
|
|
1418
|
+
"]"
|
|
1419
|
+
]
|
|
1420
|
+
})]
|
|
1421
|
+
})
|
|
1422
|
+
}, field.path))
|
|
1423
|
+
})]
|
|
1424
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
1425
|
+
className: "p-3 border border-default rounded-md bg-muted",
|
|
1426
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
1427
|
+
className: "text-sm text-muted-foreground",
|
|
1428
|
+
children: tr("fluidgrids.builder.mappingDialog.noArrayFieldsHint", "No array fields found in schema. Enter the array path manually:")
|
|
1429
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
1430
|
+
placeholder: tr("fluidgrids.builder.mappingDialog.arrayPathPlaceholder", "e.g., searchEmails.emails"),
|
|
1431
|
+
value: selectedSourceArray,
|
|
1432
|
+
onChange: (e) => handleSourceArrayChange(e.target.value),
|
|
1433
|
+
className: "mt-2 font-mono"
|
|
1434
|
+
})]
|
|
1435
|
+
})]
|
|
1436
|
+
}),
|
|
1437
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1438
|
+
className: "grid gap-2",
|
|
1439
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.stepExtractProperty", "2. Extract Property from Each Item") }), arrayItemFields.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
1440
|
+
className: "space-y-2",
|
|
1441
|
+
children: [
|
|
1442
|
+
/* @__PURE__ */ jsx("div", {
|
|
1443
|
+
className: "flex flex-wrap gap-1 max-h-[120px] overflow-y-auto p-1",
|
|
1444
|
+
children: arrayItemFields.map((field) => /* @__PURE__ */ jsxs(Badge, {
|
|
1445
|
+
variant: itemExpression === field.path ? "default" : "outline",
|
|
1446
|
+
className: `cursor-pointer hover:bg-primary hover:text-primary-foreground ${field.isNested ? "ml-2 border-dashed" : ""}`,
|
|
1447
|
+
onClick: () => handleItemExpressionChange(field.path),
|
|
1448
|
+
children: [field.isNested ? /* @__PURE__ */ jsx("span", {
|
|
1449
|
+
className: "font-mono text-xs",
|
|
1450
|
+
children: field.path
|
|
1451
|
+
}) : field.name, /* @__PURE__ */ jsxs("span", {
|
|
1452
|
+
className: "ml-1 opacity-60 text-[10px]",
|
|
1453
|
+
children: [
|
|
1454
|
+
"(",
|
|
1455
|
+
field.type,
|
|
1456
|
+
")"
|
|
1457
|
+
]
|
|
1458
|
+
})]
|
|
1459
|
+
}, field.path))
|
|
1460
|
+
}),
|
|
1461
|
+
/* @__PURE__ */ jsx("p", {
|
|
1462
|
+
className: "text-xs text-muted-foreground",
|
|
1463
|
+
children: tr("fluidgrids.builder.mappingDialog.clickFieldOrCustomPath", "Click a field above or enter a custom path:")
|
|
1464
|
+
}),
|
|
1465
|
+
/* @__PURE__ */ jsx(Input, {
|
|
1466
|
+
placeholder: tr("fluidgrids.builder.mappingDialog.itemPropertyPlaceholder", "e.g., metadata.id, user.email"),
|
|
1467
|
+
value: itemExpression,
|
|
1468
|
+
onChange: (e) => handleItemExpressionChange(e.target.value),
|
|
1469
|
+
className: "font-mono"
|
|
1470
|
+
})
|
|
1471
|
+
]
|
|
1472
|
+
}) : /* @__PURE__ */ jsx(Input, {
|
|
1473
|
+
placeholder: tr("fluidgrids.builder.mappingDialog.itemPropertyPlaceholderShort", "e.g., metadata.id, user.email, id"),
|
|
1474
|
+
value: itemExpression,
|
|
1475
|
+
onChange: (e) => handleItemExpressionChange(e.target.value),
|
|
1476
|
+
className: "font-mono"
|
|
1477
|
+
})]
|
|
1478
|
+
}),
|
|
1479
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1480
|
+
className: "grid gap-2",
|
|
1481
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.stepGeneratedExpression", "3. Generated Expression") }), /* @__PURE__ */ jsx("div", {
|
|
1482
|
+
className: "p-3 bg-muted rounded-md border border-default",
|
|
1483
|
+
children: /* @__PURE__ */ jsx("code", {
|
|
1484
|
+
className: "text-sm break-all block font-mono",
|
|
1485
|
+
children: expression || "${output.arrayField.map(item => item.property)}"
|
|
1486
|
+
})
|
|
1487
|
+
})]
|
|
1488
|
+
}),
|
|
1489
|
+
selectedSourceArray && itemExpression && /* @__PURE__ */ jsxs("div", {
|
|
1490
|
+
className: "p-3 bg-[var(--color-status-success-bg-subtle)] rounded-md border border-[var(--color-status-success-border)]",
|
|
1491
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
1492
|
+
className: "text-[var(--color-status-success-text)]",
|
|
1493
|
+
children: tr("fluidgrids.builder.mappingDialog.resultPreview", "Result Preview:")
|
|
1494
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1495
|
+
className: "mt-2 flex items-center gap-2 text-sm",
|
|
1496
|
+
children: [
|
|
1497
|
+
/* @__PURE__ */ jsx("code", {
|
|
1498
|
+
className: "bg-surface px-2 py-1 rounded",
|
|
1499
|
+
children: targetFieldName || "result"
|
|
1500
|
+
}),
|
|
1501
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1502
|
+
/* @__PURE__ */ jsxs("code", {
|
|
1503
|
+
className: "bg-surface px-2 py-1 rounded",
|
|
1504
|
+
children: [
|
|
1505
|
+
"[",
|
|
1506
|
+
itemExpression,
|
|
1507
|
+
", ",
|
|
1508
|
+
itemExpression,
|
|
1509
|
+
", ...]"
|
|
1510
|
+
]
|
|
1511
|
+
})
|
|
1512
|
+
]
|
|
1513
|
+
})]
|
|
1514
|
+
})
|
|
1515
|
+
] }),
|
|
1516
|
+
mappingType !== "array" && /* @__PURE__ */ jsx("div", {
|
|
1517
|
+
className: "p-4 bg-muted rounded-md",
|
|
1518
|
+
children: /* @__PURE__ */ jsxs("p", {
|
|
1519
|
+
className: "text-sm text-muted-foreground",
|
|
1520
|
+
children: [
|
|
1521
|
+
"The guided builder is optimized for ",
|
|
1522
|
+
/* @__PURE__ */ jsx("strong", { children: "Array" }),
|
|
1523
|
+
" ",
|
|
1524
|
+
"mappings. For ",
|
|
1525
|
+
mappingType,
|
|
1526
|
+
" mappings, use the",
|
|
1527
|
+
" ",
|
|
1528
|
+
/* @__PURE__ */ jsx("strong", { children: "Examples" }),
|
|
1529
|
+
" or ",
|
|
1530
|
+
/* @__PURE__ */ jsx("strong", { children: "Editor" }),
|
|
1531
|
+
" tabs."
|
|
1532
|
+
]
|
|
1533
|
+
})
|
|
1534
|
+
}),
|
|
1535
|
+
sourceFields.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
1536
|
+
className: "grid gap-2",
|
|
1537
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.availableSourceFields", "Available Source Fields") }), /* @__PURE__ */ jsx("div", {
|
|
1538
|
+
className: "p-3 border rounded-md max-h-[150px] overflow-y-auto bg-muted/50",
|
|
1539
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1540
|
+
className: "space-y-1",
|
|
1541
|
+
children: [sourceFields.slice(0, 15).map((field) => /* @__PURE__ */ jsxs("div", {
|
|
1542
|
+
className: "flex items-center gap-2 text-xs",
|
|
1543
|
+
children: [/* @__PURE__ */ jsx("code", {
|
|
1544
|
+
className: "font-mono",
|
|
1545
|
+
children: field.path
|
|
1546
|
+
}), /* @__PURE__ */ jsx(Badge, {
|
|
1547
|
+
variant: "outline",
|
|
1548
|
+
className: "text-[10px]",
|
|
1549
|
+
children: field.isList ? `[${field.type}]` : field.type
|
|
1550
|
+
})]
|
|
1551
|
+
}, field.path)), sourceFields.length > 15 && /* @__PURE__ */ jsxs("p", {
|
|
1552
|
+
className: "text-xs text-muted-foreground mt-2",
|
|
1553
|
+
children: [
|
|
1554
|
+
"...and ",
|
|
1555
|
+
sourceFields.length - 15,
|
|
1556
|
+
" more fields"
|
|
1557
|
+
]
|
|
1558
|
+
})]
|
|
1559
|
+
})
|
|
1560
|
+
})]
|
|
1561
|
+
})
|
|
1562
|
+
]
|
|
1563
|
+
}),
|
|
1564
|
+
/* @__PURE__ */ jsxs(TabsContent, {
|
|
1565
|
+
value: "examples",
|
|
1566
|
+
className: "space-y-4 mt-4",
|
|
1567
|
+
children: [examples.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
1568
|
+
className: "grid gap-2",
|
|
1569
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.selectExampleLabel", "Select an example to start with:") }), /* @__PURE__ */ jsxs(Select, {
|
|
1570
|
+
value: selectedExample,
|
|
1571
|
+
onValueChange: handleExampleChange,
|
|
1572
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: tr("fluidgrids.builder.mappingDialog.chooseExamplePlaceholder", "Choose an example") }) }), /* @__PURE__ */ jsxs(SelectContent, {
|
|
1573
|
+
className: "z-[100003]",
|
|
1574
|
+
children: [/* @__PURE__ */ jsx(SelectItem, {
|
|
1575
|
+
value: "choose",
|
|
1576
|
+
children: tr("fluidgrids.builder.mappingDialog.chooseExamplePlaceholder", "Choose an example")
|
|
1577
|
+
}), examples.map((example) => /* @__PURE__ */ jsxs(SelectItem, {
|
|
1578
|
+
value: example.name,
|
|
1579
|
+
children: [
|
|
1580
|
+
example.name,
|
|
1581
|
+
" - ",
|
|
1582
|
+
example.description
|
|
1583
|
+
]
|
|
1584
|
+
}, example.name))]
|
|
1585
|
+
})]
|
|
1586
|
+
})]
|
|
1587
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1588
|
+
className: "grid gap-2",
|
|
1589
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.quickExamples", "Quick Examples:") }), /* @__PURE__ */ jsx("div", {
|
|
1590
|
+
className: "grid grid-cols-1 gap-2 max-h-[200px] overflow-y-auto",
|
|
1591
|
+
children: examples.slice(0, 8).map((example) => /* @__PURE__ */ jsxs("div", {
|
|
1592
|
+
className: "p-2 border rounded-md cursor-pointer hover:bg-muted transition-colors",
|
|
1593
|
+
onClick: () => {
|
|
1594
|
+
setExpression(example.expression);
|
|
1595
|
+
setActiveTab("editor");
|
|
1596
|
+
},
|
|
1597
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1598
|
+
className: "font-medium text-sm",
|
|
1599
|
+
children: example.name
|
|
1600
|
+
}), /* @__PURE__ */ jsx("code", {
|
|
1601
|
+
className: "text-xs text-muted-foreground block mt-1 break-all",
|
|
1602
|
+
children: example.expression
|
|
1603
|
+
})]
|
|
1604
|
+
}, example.name))
|
|
1605
|
+
})]
|
|
1606
|
+
})]
|
|
1607
|
+
}),
|
|
1608
|
+
/* @__PURE__ */ jsxs(TabsContent, {
|
|
1609
|
+
value: "editor",
|
|
1610
|
+
className: "space-y-4 mt-4",
|
|
1611
|
+
children: [
|
|
1612
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1613
|
+
className: "grid gap-2",
|
|
1614
|
+
children: [/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.builder.mappingDialog.quickInsert", "Quick Insert:") }), /* @__PURE__ */ jsx("div", {
|
|
1615
|
+
className: "flex flex-wrap gap-1",
|
|
1616
|
+
children: getQuickInserts().map((item) => /* @__PURE__ */ jsx(Badge, {
|
|
1617
|
+
variant: "secondary",
|
|
1618
|
+
className: "cursor-pointer hover:bg-primary hover:text-primary-foreground",
|
|
1619
|
+
onClick: () => insertSnippet(item.snippet),
|
|
1620
|
+
children: item.label
|
|
1621
|
+
}, item.label))
|
|
1622
|
+
})]
|
|
1623
|
+
}),
|
|
1624
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1625
|
+
className: "grid gap-2",
|
|
1626
|
+
children: [
|
|
1627
|
+
/* @__PURE__ */ jsx(Label, {
|
|
1628
|
+
htmlFor: "expression-input",
|
|
1629
|
+
children: tr("fluidgrids.builder.mappingDialog.templateExpression", "Template Expression")
|
|
1630
|
+
}),
|
|
1631
|
+
/* @__PURE__ */ jsx(Textarea, {
|
|
1632
|
+
id: "expression-input",
|
|
1633
|
+
placeholder: getPlaceholder(),
|
|
1634
|
+
value: expression,
|
|
1635
|
+
onChange: (e) => setExpression(e.target.value),
|
|
1636
|
+
className: "h-32 font-mono text-sm"
|
|
1637
|
+
}),
|
|
1638
|
+
/* @__PURE__ */ jsxs("p", {
|
|
1639
|
+
className: "text-xs text-muted-foreground",
|
|
1640
|
+
children: [
|
|
1641
|
+
tr("fluidgrids.builder.mappingDialog.templateSyntaxHint", "Use JavaScript template literal syntax:"),
|
|
1642
|
+
" ",
|
|
1643
|
+
/* @__PURE__ */ jsxs("code", {
|
|
1644
|
+
className: "bg-muted px-1 rounded",
|
|
1645
|
+
children: [
|
|
1646
|
+
"$",
|
|
1647
|
+
"{",
|
|
1648
|
+
"expression",
|
|
1649
|
+
"}"
|
|
1650
|
+
]
|
|
1651
|
+
})
|
|
1652
|
+
]
|
|
1653
|
+
})
|
|
1654
|
+
]
|
|
1655
|
+
}),
|
|
1656
|
+
(sourceField || targetField) && /* @__PURE__ */ jsxs("div", {
|
|
1657
|
+
className: "p-3 bg-muted rounded-md",
|
|
1658
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
1659
|
+
className: "text-sm font-medium mb-2",
|
|
1660
|
+
children: tr("fluidgrids.builder.mappingDialog.mappingContext", "Mapping Context")
|
|
1661
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1662
|
+
className: "text-xs space-y-1",
|
|
1663
|
+
children: [sourceField && /* @__PURE__ */ jsxs("div", { children: [
|
|
1664
|
+
/* @__PURE__ */ jsx("span", {
|
|
1665
|
+
className: "text-muted-foreground",
|
|
1666
|
+
children: tr("fluidgrids.builder.mappingDialog.sourceFieldLabel", "Source field:")
|
|
1667
|
+
}),
|
|
1668
|
+
" ",
|
|
1669
|
+
/* @__PURE__ */ jsx("code", {
|
|
1670
|
+
className: "bg-background px-1 rounded",
|
|
1671
|
+
children: sourceField
|
|
1672
|
+
})
|
|
1673
|
+
] }), targetField && /* @__PURE__ */ jsxs("div", { children: [
|
|
1674
|
+
/* @__PURE__ */ jsx("span", {
|
|
1675
|
+
className: "text-muted-foreground",
|
|
1676
|
+
children: tr("fluidgrids.builder.mappingDialog.targetFieldLabel", "Target field:")
|
|
1677
|
+
}),
|
|
1678
|
+
" ",
|
|
1679
|
+
/* @__PURE__ */ jsx("code", {
|
|
1680
|
+
className: "bg-background px-1 rounded",
|
|
1681
|
+
children: targetField
|
|
1682
|
+
})
|
|
1683
|
+
] })]
|
|
1684
|
+
})]
|
|
1685
|
+
}),
|
|
1686
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1687
|
+
className: "p-3 bg-muted rounded-md",
|
|
1688
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
1689
|
+
className: "text-sm font-medium mb-2",
|
|
1690
|
+
children: tr("fluidgrids.builder.mappingDialog.mappingValue", "Mapping Value")
|
|
1691
|
+
}), /* @__PURE__ */ jsx("code", {
|
|
1692
|
+
className: "text-xs break-all block",
|
|
1693
|
+
children: expression || tr("fluidgrids.builder.mappingDialog.emptyExpression", "(empty)")
|
|
1694
|
+
})]
|
|
1695
|
+
})
|
|
1696
|
+
]
|
|
1697
|
+
}),
|
|
1698
|
+
/* @__PURE__ */ jsxs(TabsContent, {
|
|
1699
|
+
value: "help",
|
|
1700
|
+
className: "space-y-4 mt-4",
|
|
1701
|
+
children: [
|
|
1702
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1703
|
+
className: "p-3 bg-muted rounded-md",
|
|
1704
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
1705
|
+
className: "text-sm font-medium mb-2",
|
|
1706
|
+
children: tr("fluidgrids.builder.mappingDialog.availableVariables", "Available Variables")
|
|
1707
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1708
|
+
className: "text-xs space-y-2",
|
|
1709
|
+
children: [
|
|
1710
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1711
|
+
/* @__PURE__ */ jsx("code", {
|
|
1712
|
+
className: "bg-background px-1 rounded",
|
|
1713
|
+
children: "output"
|
|
1714
|
+
}),
|
|
1715
|
+
" -",
|
|
1716
|
+
" ",
|
|
1717
|
+
tr("fluidgrids.builder.mappingDialog.varOutput", "Output from the previous node")
|
|
1718
|
+
] }),
|
|
1719
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1720
|
+
/* @__PURE__ */ jsx("code", {
|
|
1721
|
+
className: "bg-background px-1 rounded",
|
|
1722
|
+
children: "TriggerInput"
|
|
1723
|
+
}),
|
|
1724
|
+
" ",
|
|
1725
|
+
"-",
|
|
1726
|
+
" ",
|
|
1727
|
+
tr("fluidgrids.builder.mappingDialog.varTriggerInput", "Original trigger payload")
|
|
1728
|
+
] }),
|
|
1729
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1730
|
+
/* @__PURE__ */ jsx("code", {
|
|
1731
|
+
className: "bg-background px-1 rounded",
|
|
1732
|
+
children: "WorkflowState"
|
|
1733
|
+
}),
|
|
1734
|
+
" ",
|
|
1735
|
+
"-",
|
|
1736
|
+
" ",
|
|
1737
|
+
tr("fluidgrids.builder.mappingDialog.varWorkflowState", "Outputs from all previous nodes")
|
|
1738
|
+
] }),
|
|
1739
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1740
|
+
/* @__PURE__ */ jsx("code", {
|
|
1741
|
+
className: "bg-background px-1 rounded",
|
|
1742
|
+
children: "RunContext"
|
|
1743
|
+
}),
|
|
1744
|
+
" ",
|
|
1745
|
+
"-",
|
|
1746
|
+
" ",
|
|
1747
|
+
tr("fluidgrids.builder.mappingDialog.varRunContext", "Workflow execution metadata")
|
|
1748
|
+
] }),
|
|
1749
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1750
|
+
/* @__PURE__ */ jsx("code", {
|
|
1751
|
+
className: "bg-background px-1 rounded",
|
|
1752
|
+
children: "item"
|
|
1753
|
+
}),
|
|
1754
|
+
" -",
|
|
1755
|
+
" ",
|
|
1756
|
+
tr("fluidgrids.builder.mappingDialog.varItem", "Current item (in array operations)")
|
|
1757
|
+
] }),
|
|
1758
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1759
|
+
/* @__PURE__ */ jsx("code", {
|
|
1760
|
+
className: "bg-background px-1 rounded",
|
|
1761
|
+
children: "index"
|
|
1762
|
+
}),
|
|
1763
|
+
" -",
|
|
1764
|
+
" ",
|
|
1765
|
+
tr("fluidgrids.builder.mappingDialog.varIndex", "Current index (in array operations)")
|
|
1766
|
+
] })
|
|
1767
|
+
]
|
|
1768
|
+
})]
|
|
1769
|
+
}),
|
|
1770
|
+
safeBuiltins.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
1771
|
+
className: "p-3 bg-muted rounded-md",
|
|
1772
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
1773
|
+
className: "text-sm font-medium mb-2",
|
|
1774
|
+
children: tr("fluidgrids.builder.mappingDialog.availableFunctions", "Available Functions")
|
|
1775
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1776
|
+
className: "text-xs grid grid-cols-3 gap-1",
|
|
1777
|
+
children: safeBuiltins.map((func) => /* @__PURE__ */ jsx("code", {
|
|
1778
|
+
className: "bg-background px-1 rounded",
|
|
1779
|
+
children: func
|
|
1780
|
+
}, func))
|
|
1781
|
+
})]
|
|
1782
|
+
}),
|
|
1783
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1784
|
+
className: "p-3 bg-muted rounded-md",
|
|
1785
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
1786
|
+
className: "text-sm font-medium mb-2",
|
|
1787
|
+
children: tr("fluidgrids.builder.mappingDialog.syntaxTips", "Syntax Tips")
|
|
1788
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1789
|
+
className: "text-xs space-y-1",
|
|
1790
|
+
children: [
|
|
1791
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1792
|
+
/* @__PURE__ */ jsx("strong", { children: tr("fluidgrids.builder.mappingDialog.tipOptionalChaining", "Optional chaining:") }),
|
|
1793
|
+
" ",
|
|
1794
|
+
/* @__PURE__ */ jsx("code", {
|
|
1795
|
+
className: "bg-background px-1 rounded",
|
|
1796
|
+
children: "output.user?.email"
|
|
1797
|
+
})
|
|
1798
|
+
] }),
|
|
1799
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1800
|
+
/* @__PURE__ */ jsx("strong", { children: tr("fluidgrids.builder.mappingDialog.tipNullishCoalescing", "Nullish coalescing:") }),
|
|
1801
|
+
" ",
|
|
1802
|
+
/* @__PURE__ */ jsx("code", {
|
|
1803
|
+
className: "bg-background px-1 rounded",
|
|
1804
|
+
children: `output.value ?? 'default'`
|
|
1805
|
+
})
|
|
1806
|
+
] }),
|
|
1807
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1808
|
+
/* @__PURE__ */ jsx("strong", { children: tr("fluidgrids.builder.mappingDialog.tipTernary", "Ternary:") }),
|
|
1809
|
+
" ",
|
|
1810
|
+
/* @__PURE__ */ jsx("code", {
|
|
1811
|
+
className: "bg-background px-1 rounded",
|
|
1812
|
+
children: `output.active ? 'Yes' : 'No'`
|
|
1813
|
+
})
|
|
1814
|
+
] }),
|
|
1815
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1816
|
+
/* @__PURE__ */ jsx("strong", { children: tr("fluidgrids.builder.mappingDialog.tipArrayAccess", "Array access:") }),
|
|
1817
|
+
" ",
|
|
1818
|
+
/* @__PURE__ */ jsx("code", {
|
|
1819
|
+
className: "bg-background px-1 rounded",
|
|
1820
|
+
children: "output.items[0].name"
|
|
1821
|
+
})
|
|
1822
|
+
] })
|
|
1823
|
+
]
|
|
1824
|
+
})]
|
|
1825
|
+
})
|
|
1826
|
+
]
|
|
1827
|
+
})
|
|
1828
|
+
]
|
|
1829
|
+
})]
|
|
1830
|
+
}),
|
|
1831
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1832
|
+
className: "p-6 border-t",
|
|
1833
|
+
children: [!isFormValid && /* @__PURE__ */ jsx("div", {
|
|
1834
|
+
className: "mb-3 p-2 bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)] rounded-md",
|
|
1835
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
1836
|
+
className: "text-sm text-[var(--color-status-warning-text)]",
|
|
1837
|
+
children: getValidationMessage()
|
|
1838
|
+
})
|
|
1839
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1840
|
+
className: "flex justify-between items-center",
|
|
1841
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1842
|
+
className: "text-xs text-muted-foreground",
|
|
1843
|
+
children: targetFieldName && expression && /* @__PURE__ */ jsxs("code", {
|
|
1844
|
+
className: "bg-muted px-2 py-1 rounded",
|
|
1845
|
+
children: [
|
|
1846
|
+
targetFieldName,
|
|
1847
|
+
":",
|
|
1848
|
+
" ",
|
|
1849
|
+
expression.length > 40 ? expression.substring(0, 40) + "..." : expression
|
|
1850
|
+
]
|
|
1851
|
+
})
|
|
1852
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1853
|
+
className: "flex gap-2",
|
|
1854
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
1855
|
+
variant: "outline",
|
|
1856
|
+
onClick: () => onOpenChange(false),
|
|
1857
|
+
children: tr("fluidgrids.builder.mappingDialog.cancel", "Cancel")
|
|
1858
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1859
|
+
onClick: handleSave,
|
|
1860
|
+
disabled: !isFormValid,
|
|
1861
|
+
children: tr("fluidgrids.builder.mappingDialog.applyMapping", "Apply Mapping")
|
|
1862
|
+
})]
|
|
1863
|
+
})]
|
|
1864
|
+
})]
|
|
1865
|
+
})
|
|
1866
|
+
]
|
|
1867
|
+
})
|
|
1868
|
+
}), document.body);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// src/builder/components/lib/mapping-utils.ts
|
|
1872
|
+
const DEFAULT_CONFIG = {
|
|
1873
|
+
timeout: 5e3,
|
|
1874
|
+
maxTemplateLength: 1e4,
|
|
1875
|
+
debug: false
|
|
1876
|
+
};
|
|
1877
|
+
/**
|
|
1878
|
+
* Safe built-ins available in the sandbox
|
|
1879
|
+
*/
|
|
1880
|
+
const SAFE_GLOBALS = {
|
|
1881
|
+
parseInt,
|
|
1882
|
+
parseFloat,
|
|
1883
|
+
isNaN,
|
|
1884
|
+
isFinite,
|
|
1885
|
+
Number,
|
|
1886
|
+
String,
|
|
1887
|
+
Boolean,
|
|
1888
|
+
JSON: {
|
|
1889
|
+
parse: JSON.parse,
|
|
1890
|
+
stringify: JSON.stringify
|
|
1891
|
+
},
|
|
1892
|
+
Date,
|
|
1893
|
+
Math,
|
|
1894
|
+
Array,
|
|
1895
|
+
Object,
|
|
1896
|
+
console: {
|
|
1897
|
+
log: (..._args) => {},
|
|
1898
|
+
error: (...args) => console.error("[Mapper]", ...args),
|
|
1899
|
+
warn: (...args) => console.warn("[Mapper]", ...args)
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
/**
|
|
1903
|
+
* Parse a template string to extract the expression
|
|
1904
|
+
* Template literals must be in the format: ${expression}
|
|
1905
|
+
*/
|
|
1906
|
+
function parseTemplate(template) {
|
|
1907
|
+
if (typeof template !== "string") return {
|
|
1908
|
+
isTemplate: false,
|
|
1909
|
+
expression: null,
|
|
1910
|
+
error: null
|
|
1911
|
+
};
|
|
1912
|
+
const match = template.match(/^\$\{(.+)\}$/s);
|
|
1913
|
+
if (!match) return {
|
|
1914
|
+
isTemplate: false,
|
|
1915
|
+
expression: null,
|
|
1916
|
+
error: null
|
|
1917
|
+
};
|
|
1918
|
+
const expression = match[1].trim();
|
|
1919
|
+
if (!expression) return {
|
|
1920
|
+
isTemplate: true,
|
|
1921
|
+
expression: null,
|
|
1922
|
+
error: "Empty template expression"
|
|
1923
|
+
};
|
|
1924
|
+
return {
|
|
1925
|
+
isTemplate: true,
|
|
1926
|
+
expression,
|
|
1927
|
+
error: null
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Build the sandbox with safe globals and scope variables
|
|
1932
|
+
*/
|
|
1933
|
+
function buildSandbox(scope) {
|
|
1934
|
+
return {
|
|
1935
|
+
...SAFE_GLOBALS,
|
|
1936
|
+
output: scope.output,
|
|
1937
|
+
TriggerInput: scope.TriggerInput,
|
|
1938
|
+
WorkflowState: scope.WorkflowState,
|
|
1939
|
+
RunContext: scope.RunContext,
|
|
1940
|
+
item: scope.item,
|
|
1941
|
+
index: scope.index
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Evaluate a JavaScript expression in a sandboxed environment
|
|
1946
|
+
*/
|
|
1947
|
+
async function evaluateExpression(expression, scope, config = DEFAULT_CONFIG) {
|
|
1948
|
+
const sandbox = buildSandbox(scope);
|
|
1949
|
+
const paramNames = Object.keys(sandbox);
|
|
1950
|
+
const paramValues = Object.values(sandbox);
|
|
1951
|
+
try {
|
|
1952
|
+
const fn = new Function(...paramNames, `"use strict"; return (${expression});`);
|
|
1953
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1954
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("Execution timeout exceeded")), config.timeout || DEFAULT_CONFIG.timeout);
|
|
1955
|
+
});
|
|
1956
|
+
const resultPromise = Promise.resolve(fn(...paramValues));
|
|
1957
|
+
return await Promise.race([resultPromise, timeoutPromise]);
|
|
1958
|
+
} catch (error) {
|
|
1959
|
+
if (config.debug) console.error("[Mapper] Expression evaluation error:", error);
|
|
1960
|
+
throw error;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Map a definition object using template literals
|
|
1965
|
+
* This is the main entry point for applying mappings
|
|
1966
|
+
*/
|
|
1967
|
+
async function mapDefinition(definition, scope, config = DEFAULT_CONFIG) {
|
|
1968
|
+
if (definition === null || definition === void 0) return definition;
|
|
1969
|
+
if (typeof definition !== "object" && typeof definition !== "string") return definition;
|
|
1970
|
+
if (typeof definition === "string") {
|
|
1971
|
+
const parseResult = parseTemplate(definition);
|
|
1972
|
+
if (!parseResult.isTemplate) return definition;
|
|
1973
|
+
if (parseResult.error) throw new Error(parseResult.error);
|
|
1974
|
+
return evaluateExpression(parseResult.expression, scope, config);
|
|
1975
|
+
}
|
|
1976
|
+
if (Array.isArray(definition)) return await Promise.all(definition.map((item) => mapDefinition(item, scope, config)));
|
|
1977
|
+
const result = {};
|
|
1978
|
+
for (const key of Object.keys(definition)) result[key] = await mapDefinition(definition[key], scope, config);
|
|
1979
|
+
return result;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Validate a mapping definition for syntax errors
|
|
1983
|
+
* Returns an array of error messages
|
|
1984
|
+
*/
|
|
1985
|
+
function validateMapping(definition, path = "root") {
|
|
1986
|
+
const errors = [];
|
|
1987
|
+
if (definition === null || definition === void 0) return errors;
|
|
1988
|
+
if (typeof definition === "string") {
|
|
1989
|
+
const parseResult = parseTemplate(definition);
|
|
1990
|
+
if (parseResult.isTemplate && parseResult.error) errors.push(`${path}: ${parseResult.error}`);
|
|
1991
|
+
return errors;
|
|
1992
|
+
}
|
|
1993
|
+
if (Array.isArray(definition)) {
|
|
1994
|
+
definition.forEach((item, index) => {
|
|
1995
|
+
errors.push(...validateMapping(item, `${path}[${index}]`));
|
|
1996
|
+
});
|
|
1997
|
+
return errors;
|
|
1998
|
+
}
|
|
1999
|
+
if (typeof definition === "object") for (const key of Object.keys(definition)) errors.push(...validateMapping(definition[key], `${path}.${key}`));
|
|
2000
|
+
return errors;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Generate default mapping between source and target schemas
|
|
2004
|
+
*/
|
|
2005
|
+
function generateDefaultMapping(sourceSchema, sourceNode, sourceOperation, targetSchema, targetNode, targetOperation) {
|
|
2006
|
+
const mapping = {};
|
|
2007
|
+
const sourceReturnTypeName = getReturnTypeForOperation(sourceNode, sourceOperation);
|
|
2008
|
+
let sourceOutputType = null;
|
|
2009
|
+
if (sourceReturnTypeName) sourceOutputType = sourceSchema.types[sourceReturnTypeName];
|
|
2010
|
+
else {
|
|
2011
|
+
let sourceOperationField = null;
|
|
2012
|
+
if (sourceSchema.queryType && sourceSchema.types[sourceSchema.queryType]?.fields[sourceOperation]) sourceOperationField = sourceSchema.types[sourceSchema.queryType].fields[sourceOperation];
|
|
2013
|
+
else if (sourceSchema.mutationType && sourceSchema.types[sourceSchema.mutationType]?.fields[sourceOperation]) sourceOperationField = sourceSchema.types[sourceSchema.mutationType].fields[sourceOperation];
|
|
2014
|
+
if (sourceOperationField) {
|
|
2015
|
+
const sourceOutputTypeName = sourceOperationField.type;
|
|
2016
|
+
sourceOutputType = sourceSchema.types[sourceOutputTypeName];
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
const targetInputTypeName = getInputTypeForOperation(targetNode, targetOperation);
|
|
2020
|
+
const targetInputType = targetInputTypeName ? targetSchema.types[targetInputTypeName] : null;
|
|
2021
|
+
if (!sourceOutputType || !targetInputType) return mapping;
|
|
2022
|
+
const sourceFields = sourceOutputType.fields || {};
|
|
2023
|
+
const targetFields = targetInputType.inputFields || targetInputType.fields || {};
|
|
2024
|
+
const addMapping = (path, sourcePath) => {
|
|
2025
|
+
const templateValue = `\${output.${sourcePath}}`;
|
|
2026
|
+
if (path.includes(".")) {
|
|
2027
|
+
const pathParts = path.split(".");
|
|
2028
|
+
let currentObj = mapping;
|
|
2029
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
2030
|
+
const part = pathParts[i];
|
|
2031
|
+
if (!currentObj[part]) currentObj[part] = {};
|
|
2032
|
+
currentObj = currentObj[part];
|
|
2033
|
+
}
|
|
2034
|
+
currentObj[pathParts[pathParts.length - 1]] = templateValue;
|
|
2035
|
+
} else mapping[path] = templateValue;
|
|
2036
|
+
};
|
|
2037
|
+
const processFields = (fields, parentPath = "") => {
|
|
2038
|
+
for (const fieldName in fields) {
|
|
2039
|
+
const field = fields[fieldName];
|
|
2040
|
+
const fullPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
|
|
2041
|
+
if (field.type && targetSchema.types[field.type] && (targetSchema.types[field.type].kind === "type" || targetSchema.types[field.type].kind === "input")) {
|
|
2042
|
+
const nestedFields = targetSchema.types[field.type].fields || targetSchema.types[field.type].inputFields || {};
|
|
2043
|
+
processFields(nestedFields, fullPath);
|
|
2044
|
+
} else {
|
|
2045
|
+
if (sourceFields[fieldName]) {
|
|
2046
|
+
addMapping(fullPath, fieldName);
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
const lowerTargetField = fieldName.toLowerCase();
|
|
2050
|
+
for (const sourceFieldName in sourceFields) if (sourceFieldName.toLowerCase() === lowerTargetField) {
|
|
2051
|
+
addMapping(fullPath, sourceFieldName);
|
|
2052
|
+
break;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
processFields(targetFields);
|
|
2058
|
+
return mapping;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Apply a mapping to input data using the new template literal syntax
|
|
2062
|
+
* This is used for preview/testing in the UI
|
|
2063
|
+
*/
|
|
2064
|
+
async function applyMapping(mapping, inputData, config = DEFAULT_CONFIG) {
|
|
2065
|
+
return await mapDefinition(mapping, {
|
|
2066
|
+
output: inputData,
|
|
2067
|
+
TriggerInput: {},
|
|
2068
|
+
WorkflowState: {},
|
|
2069
|
+
RunContext: {
|
|
2070
|
+
runId: "preview-run",
|
|
2071
|
+
workflowKey: "preview-workflow",
|
|
2072
|
+
version: "1.0.0",
|
|
2073
|
+
tenantId: "preview-tenant",
|
|
2074
|
+
workspaceId: "preview-workspace",
|
|
2075
|
+
triggeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2076
|
+
}
|
|
2077
|
+
}, config);
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
// src/builder/components/mapper/visual-mapper.tsx
|
|
2081
|
+
const nodeTypes = {
|
|
2082
|
+
schemaNode: SchemaNode,
|
|
2083
|
+
nodeGroup: NodeGroup
|
|
2084
|
+
};
|
|
2085
|
+
const edgeTypes = { mappingEdge: MappingEdge };
|
|
2086
|
+
function isSchemaNodeData(data) {
|
|
2087
|
+
return data !== null && typeof data === "object" && "path" in data && "type" in data && "side" in data;
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Detect the mapping type from a template literal expression
|
|
2091
|
+
*/
|
|
2092
|
+
function detectMappingType(value) {
|
|
2093
|
+
if (typeof value !== "string") return "direct";
|
|
2094
|
+
const parseResult = parseTemplate(value);
|
|
2095
|
+
if (!parseResult.isTemplate) return "direct";
|
|
2096
|
+
const expr = parseResult.expression || "";
|
|
2097
|
+
if (expr.includes(".map(") || expr.includes(".filter(") || expr.includes(".reduce(") || expr.includes(".find(") || expr.includes(".some(") || expr.includes(".every(") || expr.includes(".flat(") || expr.includes(".flatMap(") || expr.includes(".sort(")) return "array";
|
|
2098
|
+
if (expr.includes("Object.keys(") || expr.includes("Object.values(") || expr.includes("Object.entries(") || expr.includes("Object.assign(") || expr.includes("Object.fromEntries(") || expr.startsWith("({") || expr.startsWith("{ ...")) return "object";
|
|
2099
|
+
if (expr.startsWith("parseInt(") || expr.startsWith("parseFloat(") || expr.startsWith("String(") || expr.startsWith("Boolean(") || expr.startsWith("Number(")) return "cast";
|
|
2100
|
+
if (expr.includes("TriggerInput.") || expr.includes("WorkflowState.") || expr.includes("RunContext.")) return "context";
|
|
2101
|
+
if (expr.includes(".toUpperCase()") || expr.includes(".toLowerCase()") || expr.includes(".trim()") || expr.includes(".split(") || expr.includes(".join(") || expr.includes(".replace(") || expr.includes(".substring(") || expr.includes(".slice(")) return "transform";
|
|
2102
|
+
if (expr.includes(" + ") || expr.includes(" - ") || expr.includes(" * ") || expr.includes(" / ") || expr.includes(" ? ") || expr.includes(" && ") || expr.includes(" || ") || expr.includes(" ?? ") || expr.includes("Math.")) return "expression";
|
|
2103
|
+
return "direct";
|
|
2104
|
+
}
|
|
2105
|
+
function VisualMapperContent({ sourceSchema, targetSchema, sourceNode, sourceOperation, targetNode, targetOperation, mapping, onMappingChange }) {
|
|
2106
|
+
const { toast } = useToast();
|
|
2107
|
+
const tr = useTr();
|
|
2108
|
+
const reactFlowWrapper = useRef(null);
|
|
2109
|
+
const [selectedSourceNode, setSelectedSourceNode] = useState(null);
|
|
2110
|
+
const [selectedTargetNode, setSelectedTargetNode] = useState(null);
|
|
2111
|
+
const [mappingDialogOpen, setMappingDialogOpen] = useState(false);
|
|
2112
|
+
const [currentMappingType, setCurrentMappingType] = useState("expression");
|
|
2113
|
+
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
2114
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
2115
|
+
useEffect(() => {
|
|
2116
|
+
if (sourceSchema && targetSchema && sourceNode && sourceOperation && targetNode && targetOperation) {
|
|
2117
|
+
const { nodes: generatedNodes } = generateSchemaNodes(sourceSchema, sourceNode, sourceOperation, targetSchema, targetNode, targetOperation);
|
|
2118
|
+
if (generatedNodes.length === 0) toast({
|
|
2119
|
+
title: tr("fluidgrids.builder.visualMapper.noFieldsFoundTitle", "No fields found"),
|
|
2120
|
+
description: tr("fluidgrids.builder.visualMapper.noFieldsFoundDescription", "Could not find fields to map between these nodes and operations."),
|
|
2121
|
+
variant: "destructive"
|
|
2122
|
+
});
|
|
2123
|
+
else {
|
|
2124
|
+
setNodes(generatedNodes);
|
|
2125
|
+
setTimeout(() => {
|
|
2126
|
+
document.querySelector(".react-flow__controls-fitview")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
2127
|
+
}, 100);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
}, [
|
|
2131
|
+
sourceSchema,
|
|
2132
|
+
targetSchema,
|
|
2133
|
+
sourceNode,
|
|
2134
|
+
sourceOperation,
|
|
2135
|
+
targetNode,
|
|
2136
|
+
targetOperation,
|
|
2137
|
+
setNodes,
|
|
2138
|
+
toast,
|
|
2139
|
+
tr
|
|
2140
|
+
]);
|
|
2141
|
+
useEffect(() => {
|
|
2142
|
+
const newEdges = [];
|
|
2143
|
+
const processMapping = (obj, parentPath = "") => {
|
|
2144
|
+
for (const key in obj) {
|
|
2145
|
+
const value = obj[key];
|
|
2146
|
+
const currentPath = parentPath ? `${parentPath}.${key}` : key;
|
|
2147
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) processMapping(value, currentPath);
|
|
2148
|
+
else if (typeof value === "string") {
|
|
2149
|
+
const parseResult = parseTemplate(value);
|
|
2150
|
+
const mappingType = detectMappingType(value);
|
|
2151
|
+
const targetNodeObj = nodes.find((n) => isSchemaNodeData(n.data) && n.data.path === currentPath && n.data.side === "target");
|
|
2152
|
+
if (targetNodeObj && isSchemaNodeData(targetNodeObj.data)) {
|
|
2153
|
+
if (parseResult.isTemplate) {
|
|
2154
|
+
const simpleFieldMatch = (parseResult.expression || "").match(/^output\.([a-zA-Z0-9_.]+)$/);
|
|
2155
|
+
if (simpleFieldMatch && mappingType === "direct") {
|
|
2156
|
+
const sourcePath = simpleFieldMatch[1];
|
|
2157
|
+
const sourceNodeObj = nodes.find((n) => isSchemaNodeData(n.data) && n.data.path === sourcePath && n.data.side === "source");
|
|
2158
|
+
if (sourceNodeObj && isSchemaNodeData(sourceNodeObj.data)) {
|
|
2159
|
+
const isCompatible = areTypesCompatible(sourceNodeObj.data.type, targetNodeObj.data.type);
|
|
2160
|
+
newEdges.push({
|
|
2161
|
+
id: `${sourceNodeObj.id}-${targetNodeObj.id}`,
|
|
2162
|
+
source: sourceNodeObj.id,
|
|
2163
|
+
target: targetNodeObj.id,
|
|
2164
|
+
type: "mappingEdge",
|
|
2165
|
+
animated: true,
|
|
2166
|
+
data: {
|
|
2167
|
+
mappingType: "direct",
|
|
2168
|
+
isCompatible,
|
|
2169
|
+
color: compatibilityColor(isCompatible)
|
|
2170
|
+
}
|
|
2171
|
+
});
|
|
2172
|
+
continue;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
newEdges.push({
|
|
2176
|
+
id: `mapping-${currentPath}`,
|
|
2177
|
+
source: "virtual-source",
|
|
2178
|
+
target: targetNodeObj.id,
|
|
2179
|
+
type: "mappingEdge",
|
|
2180
|
+
animated: true,
|
|
2181
|
+
data: {
|
|
2182
|
+
mappingType,
|
|
2183
|
+
expression: value,
|
|
2184
|
+
color: mappingTypeColor(mappingType)
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
processMapping(mapping);
|
|
2193
|
+
setEdges(newEdges);
|
|
2194
|
+
}, [
|
|
2195
|
+
mapping,
|
|
2196
|
+
nodes,
|
|
2197
|
+
setEdges
|
|
2198
|
+
]);
|
|
2199
|
+
const onConnect = useCallback((connection) => {
|
|
2200
|
+
const sourceNodeObj = nodes.find((n) => n.id === connection.source);
|
|
2201
|
+
const targetNodeObj = nodes.find((n) => n.id === connection.target);
|
|
2202
|
+
if (sourceNodeObj && targetNodeObj && isSchemaNodeData(sourceNodeObj.data) && isSchemaNodeData(targetNodeObj.data) && sourceNodeObj.data.side === "source" && targetNodeObj.data.side === "target") {
|
|
2203
|
+
const sourceType = sourceNodeObj.data.type;
|
|
2204
|
+
const targetType = targetNodeObj.data.type;
|
|
2205
|
+
const isCompatible = areTypesCompatible(sourceType, targetType);
|
|
2206
|
+
const newEdge = {
|
|
2207
|
+
...connection,
|
|
2208
|
+
type: "mappingEdge",
|
|
2209
|
+
animated: true,
|
|
2210
|
+
data: {
|
|
2211
|
+
mappingType: "direct",
|
|
2212
|
+
isCompatible,
|
|
2213
|
+
color: compatibilityColor(isCompatible)
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
setEdges((eds) => addEdge(newEdge, eds));
|
|
2217
|
+
const updatedMapping = { ...mapping };
|
|
2218
|
+
const sourcePath = sourceNodeObj.data.path;
|
|
2219
|
+
const targetPath = targetNodeObj.data.path;
|
|
2220
|
+
const templateValue = `\${output.${sourcePath}}`;
|
|
2221
|
+
if (targetPath.includes(".")) {
|
|
2222
|
+
const pathParts = targetPath.split(".");
|
|
2223
|
+
let currentObj = updatedMapping;
|
|
2224
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
2225
|
+
const part = pathParts[i];
|
|
2226
|
+
if (!currentObj[part]) currentObj[part] = {};
|
|
2227
|
+
else if (typeof currentObj[part] !== "object") currentObj[part] = {};
|
|
2228
|
+
currentObj = currentObj[part];
|
|
2229
|
+
}
|
|
2230
|
+
currentObj[pathParts[pathParts.length - 1]] = templateValue;
|
|
2231
|
+
} else updatedMapping[targetPath] = templateValue;
|
|
2232
|
+
onMappingChange(updatedMapping);
|
|
2233
|
+
if (isCompatible) toast({
|
|
2234
|
+
title: tr("fluidgrids.builder.visualMapper.mappingCreatedTitle", "Mapping created"),
|
|
2235
|
+
description: `Mapped ${sourcePath} (${sourceType}) to ${targetPath} (${targetType})`
|
|
2236
|
+
});
|
|
2237
|
+
else toast({
|
|
2238
|
+
title: tr("fluidgrids.builder.visualMapper.typeMismatchTitle", "Type mismatch warning"),
|
|
2239
|
+
description: `Mapped ${sourcePath} (${sourceType}) to ${targetPath} (${targetType}). Types may not be compatible.`,
|
|
2240
|
+
variant: "destructive"
|
|
2241
|
+
});
|
|
2242
|
+
} else toast({
|
|
2243
|
+
title: tr("fluidgrids.builder.visualMapper.invalidMappingTitle", "Invalid mapping"),
|
|
2244
|
+
description: tr("fluidgrids.builder.visualMapper.invalidMappingDescription", "You can only map from source to target fields"),
|
|
2245
|
+
variant: "destructive"
|
|
2246
|
+
});
|
|
2247
|
+
}, [
|
|
2248
|
+
nodes,
|
|
2249
|
+
mapping,
|
|
2250
|
+
onMappingChange,
|
|
2251
|
+
toast,
|
|
2252
|
+
tr,
|
|
2253
|
+
setEdges
|
|
2254
|
+
]);
|
|
2255
|
+
const onEdgeDelete = useCallback((edge) => {
|
|
2256
|
+
const targetNodeObj = nodes.find((n) => n.id === edge.target);
|
|
2257
|
+
if (targetNodeObj && isSchemaNodeData(targetNodeObj.data)) {
|
|
2258
|
+
const targetPath = targetNodeObj.data.path;
|
|
2259
|
+
const updatedMapping = { ...mapping };
|
|
2260
|
+
if (targetPath.includes(".")) {
|
|
2261
|
+
const pathParts = targetPath.split(".");
|
|
2262
|
+
let currentObj = updatedMapping;
|
|
2263
|
+
let parentObj = null;
|
|
2264
|
+
let lastPart = null;
|
|
2265
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
2266
|
+
const part = pathParts[i];
|
|
2267
|
+
if (!currentObj[part] || typeof currentObj[part] !== "object") return;
|
|
2268
|
+
parentObj = currentObj;
|
|
2269
|
+
currentObj = currentObj[part];
|
|
2270
|
+
lastPart = pathParts[i + 1];
|
|
2271
|
+
}
|
|
2272
|
+
if (parentObj && lastPart) {
|
|
2273
|
+
delete currentObj[pathParts[pathParts.length - 1]];
|
|
2274
|
+
if (Object.keys(currentObj).length === 0 && pathParts.length > 1) delete parentObj[pathParts[pathParts.length - 2]];
|
|
2275
|
+
}
|
|
2276
|
+
} else delete updatedMapping[targetPath];
|
|
2277
|
+
onMappingChange(updatedMapping);
|
|
2278
|
+
}
|
|
2279
|
+
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
|
2280
|
+
}, [
|
|
2281
|
+
nodes,
|
|
2282
|
+
mapping,
|
|
2283
|
+
onMappingChange,
|
|
2284
|
+
setEdges
|
|
2285
|
+
]);
|
|
2286
|
+
const onNodeClick = useCallback((_event, node) => {
|
|
2287
|
+
if (node.data && typeof node.data === "object" && isSchemaNodeData(node.data)) {
|
|
2288
|
+
const schemaNode = {
|
|
2289
|
+
id: node.id,
|
|
2290
|
+
data: node.data
|
|
2291
|
+
};
|
|
2292
|
+
if (schemaNode.data.side === "source") setSelectedSourceNode(schemaNode);
|
|
2293
|
+
else if (schemaNode.data.side === "target") setSelectedTargetNode(schemaNode);
|
|
2294
|
+
}
|
|
2295
|
+
}, []);
|
|
2296
|
+
const openMappingDialog = (type) => {
|
|
2297
|
+
setCurrentMappingType(type);
|
|
2298
|
+
setMappingDialogOpen(true);
|
|
2299
|
+
};
|
|
2300
|
+
const handleSaveMappingDialog = (expression, targetFieldName) => {
|
|
2301
|
+
const targetPath = targetFieldName || selectedTargetNode?.data?.path;
|
|
2302
|
+
if (!targetPath) {
|
|
2303
|
+
toast({
|
|
2304
|
+
title: tr("fluidgrids.builder.visualMapper.noTargetFieldTitle", "No target field specified"),
|
|
2305
|
+
description: tr("fluidgrids.builder.visualMapper.noTargetFieldDescription", "Please enter a target field name or select a target field in the visual mapper"),
|
|
2306
|
+
variant: "destructive"
|
|
2307
|
+
});
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
2310
|
+
const updatedMapping = { ...mapping };
|
|
2311
|
+
if (targetPath.includes(".")) {
|
|
2312
|
+
const pathParts = targetPath.split(".");
|
|
2313
|
+
let currentObj = updatedMapping;
|
|
2314
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
2315
|
+
const part = pathParts[i];
|
|
2316
|
+
if (!currentObj[part]) currentObj[part] = {};
|
|
2317
|
+
else if (typeof currentObj[part] !== "object") currentObj[part] = {};
|
|
2318
|
+
currentObj = currentObj[part];
|
|
2319
|
+
}
|
|
2320
|
+
currentObj[pathParts[pathParts.length - 1]] = expression;
|
|
2321
|
+
} else updatedMapping[targetPath] = expression;
|
|
2322
|
+
onMappingChange(updatedMapping);
|
|
2323
|
+
const mappingType = detectMappingType(expression);
|
|
2324
|
+
toast({
|
|
2325
|
+
title: `${mappingType.charAt(0).toUpperCase() + mappingType.slice(1)} mapping created`,
|
|
2326
|
+
description: `${tr("fluidgrids.builder.visualMapper.appliedMappingTo", "Applied mapping to")} ${targetPath}`
|
|
2327
|
+
});
|
|
2328
|
+
};
|
|
2329
|
+
const mappingTypeItems = [
|
|
2330
|
+
{
|
|
2331
|
+
type: "direct",
|
|
2332
|
+
label: tr("fluidgrids.builder.visualMapper.typeDirectLabel", "Direct"),
|
|
2333
|
+
icon: Type,
|
|
2334
|
+
description: tr("fluidgrids.builder.visualMapper.typeDirectDescription", "Simple field access")
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
type: "expression",
|
|
2338
|
+
label: tr("fluidgrids.builder.visualMapper.typeExpressionLabel", "Expression"),
|
|
2339
|
+
icon: Code,
|
|
2340
|
+
description: tr("fluidgrids.builder.visualMapper.typeExpressionDescription", "Custom JavaScript expression")
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
type: "transform",
|
|
2344
|
+
label: tr("fluidgrids.builder.visualMapper.typeTransformLabel", "Transform"),
|
|
2345
|
+
icon: Type,
|
|
2346
|
+
description: tr("fluidgrids.builder.visualMapper.typeTransformDescription", "String transformations")
|
|
2347
|
+
},
|
|
2348
|
+
{
|
|
2349
|
+
type: "cast",
|
|
2350
|
+
label: tr("fluidgrids.builder.visualMapper.typeCastLabel", "Cast"),
|
|
2351
|
+
icon: Hash,
|
|
2352
|
+
description: tr("fluidgrids.builder.visualMapper.typeCastDescription", "Type conversions")
|
|
2353
|
+
},
|
|
2354
|
+
{
|
|
2355
|
+
type: "array",
|
|
2356
|
+
label: tr("fluidgrids.builder.visualMapper.typeArrayLabel", "Array"),
|
|
2357
|
+
icon: List,
|
|
2358
|
+
description: tr("fluidgrids.builder.visualMapper.typeArrayDescription", "Array operations (map, filter, reduce)")
|
|
2359
|
+
},
|
|
2360
|
+
{
|
|
2361
|
+
type: "object",
|
|
2362
|
+
label: tr("fluidgrids.builder.visualMapper.typeObjectLabel", "Object"),
|
|
2363
|
+
icon: Braces,
|
|
2364
|
+
description: tr("fluidgrids.builder.visualMapper.typeObjectDescription", "Object manipulation")
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
type: "context",
|
|
2368
|
+
label: tr("fluidgrids.builder.visualMapper.typeContextLabel", "Context"),
|
|
2369
|
+
icon: Workflow,
|
|
2370
|
+
description: tr("fluidgrids.builder.visualMapper.typeContextDescription", "Workflow state access")
|
|
2371
|
+
}
|
|
2372
|
+
];
|
|
2373
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2374
|
+
className: "h-[550px] w-full border rounded-md",
|
|
2375
|
+
ref: reactFlowWrapper,
|
|
2376
|
+
children: [/* @__PURE__ */ jsxs(ReactFlow, {
|
|
2377
|
+
nodes,
|
|
2378
|
+
edges,
|
|
2379
|
+
onNodesChange,
|
|
2380
|
+
onEdgesChange,
|
|
2381
|
+
onConnect,
|
|
2382
|
+
onEdgeClick: (_, edge) => onEdgeDelete(edge),
|
|
2383
|
+
onNodeClick,
|
|
2384
|
+
nodeTypes,
|
|
2385
|
+
edgeTypes,
|
|
2386
|
+
fitView: true,
|
|
2387
|
+
fitViewOptions: { padding: .2 },
|
|
2388
|
+
minZoom: .4,
|
|
2389
|
+
maxZoom: 1.5,
|
|
2390
|
+
defaultViewport: {
|
|
2391
|
+
x: 0,
|
|
2392
|
+
y: 0,
|
|
2393
|
+
zoom: .7
|
|
2394
|
+
},
|
|
2395
|
+
attributionPosition: "bottom-right",
|
|
2396
|
+
children: [
|
|
2397
|
+
/* @__PURE__ */ jsx(Controls, {}),
|
|
2398
|
+
/* @__PURE__ */ jsx(MiniMap, {}),
|
|
2399
|
+
/* @__PURE__ */ jsx(Background, {
|
|
2400
|
+
gap: 12,
|
|
2401
|
+
size: 1
|
|
2402
|
+
}),
|
|
2403
|
+
/* @__PURE__ */ jsx(Panel, {
|
|
2404
|
+
position: "top-right",
|
|
2405
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2406
|
+
className: "flex flex-col gap-2",
|
|
2407
|
+
children: [/* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
2408
|
+
asChild: true,
|
|
2409
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
2410
|
+
variant: "outline",
|
|
2411
|
+
size: "sm",
|
|
2412
|
+
className: "flex items-center gap-1",
|
|
2413
|
+
children: [
|
|
2414
|
+
/* @__PURE__ */ jsx(PlusCircle, { className: "h-4 w-4" }),
|
|
2415
|
+
tr("fluidgrids.builder.visualMapper.addMapping", "Add Mapping"),
|
|
2416
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
|
|
2417
|
+
]
|
|
2418
|
+
})
|
|
2419
|
+
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
2420
|
+
align: "end",
|
|
2421
|
+
className: "w-56 z-[100000]",
|
|
2422
|
+
children: [
|
|
2423
|
+
/* @__PURE__ */ jsx(DropdownMenuLabel, { children: tr("fluidgrids.builder.visualMapper.mappingTypes", "Mapping Types") }),
|
|
2424
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
2425
|
+
mappingTypeItems.map((item) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
2426
|
+
onClick: () => openMappingDialog(item.type),
|
|
2427
|
+
className: "flex items-start gap-2 cursor-pointer",
|
|
2428
|
+
children: [/* @__PURE__ */ jsx(item.icon, {
|
|
2429
|
+
className: "h-4 w-4 mt-0.5",
|
|
2430
|
+
style: { color: mappingTypeColor(item.type) }
|
|
2431
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2432
|
+
className: "flex flex-col",
|
|
2433
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2434
|
+
className: "font-medium",
|
|
2435
|
+
children: item.label
|
|
2436
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2437
|
+
className: "text-xs text-muted-foreground",
|
|
2438
|
+
children: item.description
|
|
2439
|
+
})]
|
|
2440
|
+
})]
|
|
2441
|
+
}, item.type))
|
|
2442
|
+
]
|
|
2443
|
+
})] }), selectedTargetNode && /* @__PURE__ */ jsxs("div", {
|
|
2444
|
+
className: "p-2 bg-muted rounded-md text-xs",
|
|
2445
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2446
|
+
className: "text-muted-foreground",
|
|
2447
|
+
children: tr("fluidgrids.builder.visualMapper.targetIndicator", "Target:")
|
|
2448
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
2449
|
+
className: "font-mono",
|
|
2450
|
+
children: selectedTargetNode.data.path
|
|
2451
|
+
})]
|
|
2452
|
+
})]
|
|
2453
|
+
})
|
|
2454
|
+
})
|
|
2455
|
+
]
|
|
2456
|
+
}), /* @__PURE__ */ jsx(MappingDialog, {
|
|
2457
|
+
open: mappingDialogOpen,
|
|
2458
|
+
onOpenChange: setMappingDialogOpen,
|
|
2459
|
+
onSave: handleSaveMappingDialog,
|
|
2460
|
+
mappingType: currentMappingType,
|
|
2461
|
+
sourceField: selectedSourceNode?.data?.path,
|
|
2462
|
+
targetField: selectedTargetNode?.data?.path,
|
|
2463
|
+
sourceSchema,
|
|
2464
|
+
sourceOperation,
|
|
2465
|
+
targetSchema,
|
|
2466
|
+
targetOperation
|
|
2467
|
+
})]
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
function VisualMapper(props) {
|
|
2471
|
+
return /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(VisualMapperContent, { ...props }) });
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
// src/builder/components/mapper/code-editor.tsx
|
|
2475
|
+
function CodeEditor({ value, onChange, language: _language = "json", onBlur }) {
|
|
2476
|
+
const editorRef = useRef(null);
|
|
2477
|
+
useEffect(() => {
|
|
2478
|
+
if (editorRef.current) editorRef.current.value = value;
|
|
2479
|
+
}, [value]);
|
|
2480
|
+
return /* @__PURE__ */ jsx(Card, {
|
|
2481
|
+
className: "relative ",
|
|
2482
|
+
children: /* @__PURE__ */ jsx("textarea", {
|
|
2483
|
+
ref: editorRef,
|
|
2484
|
+
className: "w-full h-[400px] p-4 font-mono bg-surface text-sm resize-none focus:outline-none focus:ring-2 focus:ring-primary transition-shadow duration-200",
|
|
2485
|
+
defaultValue: value,
|
|
2486
|
+
onChange,
|
|
2487
|
+
spellCheck: false,
|
|
2488
|
+
onBlur
|
|
2489
|
+
})
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
// src/builder/components/mapper/mapping-preview.tsx
|
|
2494
|
+
function MappingPreview({ input, mapping, output }) {
|
|
2495
|
+
const tr = useTr();
|
|
2496
|
+
const generatedOutput = useMemo(() => {
|
|
2497
|
+
try {
|
|
2498
|
+
return applyMapping(mapping, input);
|
|
2499
|
+
} catch (error) {
|
|
2500
|
+
console.error("Error applying mapping:", error);
|
|
2501
|
+
return {
|
|
2502
|
+
error: true,
|
|
2503
|
+
message: error instanceof Error ? error.message : tr("fluidgrids.builder.mappingPreview.unknownError", "Unknown error occurred while applying mapping"),
|
|
2504
|
+
input
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
}, [
|
|
2508
|
+
input,
|
|
2509
|
+
mapping,
|
|
2510
|
+
tr
|
|
2511
|
+
]);
|
|
2512
|
+
return /* @__PURE__ */ jsxs(Tabs, {
|
|
2513
|
+
defaultValue: "input",
|
|
2514
|
+
children: [
|
|
2515
|
+
/* @__PURE__ */ jsxs(TabsList, {
|
|
2516
|
+
className: "mb-4",
|
|
2517
|
+
children: [
|
|
2518
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
2519
|
+
value: "input",
|
|
2520
|
+
children: tr("fluidgrids.builder.mappingPreview.tabInput", "Input")
|
|
2521
|
+
}),
|
|
2522
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
2523
|
+
value: "mapping",
|
|
2524
|
+
children: tr("fluidgrids.builder.mappingPreview.tabMapping", "Mapping")
|
|
2525
|
+
}),
|
|
2526
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
2527
|
+
value: "output",
|
|
2528
|
+
children: tr("fluidgrids.builder.mappingPreview.tabOutput", "Output")
|
|
2529
|
+
})
|
|
2530
|
+
]
|
|
2531
|
+
}),
|
|
2532
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
2533
|
+
value: "input",
|
|
2534
|
+
children: /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, {
|
|
2535
|
+
className: "p-4",
|
|
2536
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
2537
|
+
className: "text-sm overflow-auto max-h-[400px]",
|
|
2538
|
+
children: JSON.stringify(input, null, 2)
|
|
2539
|
+
})
|
|
2540
|
+
}) })
|
|
2541
|
+
}),
|
|
2542
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
2543
|
+
value: "mapping",
|
|
2544
|
+
children: /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, {
|
|
2545
|
+
className: "p-4",
|
|
2546
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
2547
|
+
className: "text-sm overflow-auto max-h-[400px]",
|
|
2548
|
+
children: JSON.stringify(mapping, null, 2)
|
|
2549
|
+
})
|
|
2550
|
+
}) })
|
|
2551
|
+
}),
|
|
2552
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
2553
|
+
value: "output",
|
|
2554
|
+
children: /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, {
|
|
2555
|
+
className: "p-4",
|
|
2556
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
2557
|
+
className: "text-sm overflow-auto max-h-[400px]",
|
|
2558
|
+
children: output ? JSON.stringify(output, null, 2) : generatedOutput ? JSON.stringify(generatedOutput, null, 2) : tr("fluidgrids.builder.mappingPreview.noOutput", "No output generated yet")
|
|
2559
|
+
})
|
|
2560
|
+
}) })
|
|
2561
|
+
})
|
|
2562
|
+
]
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// src/builder/components/mapper/mapping-editor.tsx
|
|
2567
|
+
function generateSampleValueForType(typeName, schema) {
|
|
2568
|
+
if (typeName.startsWith("[") && typeName.endsWith("]")) return [generateSampleValueForType(typeName.substring(1, typeName.length - 1).replace(/!$/, ""), schema)];
|
|
2569
|
+
const baseType = typeName.endsWith("!") ? typeName.slice(0, -1) : typeName;
|
|
2570
|
+
switch (baseType) {
|
|
2571
|
+
case "String": return "Sample string";
|
|
2572
|
+
case "Int": return 42;
|
|
2573
|
+
case "Float": return 3.14;
|
|
2574
|
+
case "Boolean": return true;
|
|
2575
|
+
case "ID": return "id-123456";
|
|
2576
|
+
default:
|
|
2577
|
+
if (schema.types[baseType]) {
|
|
2578
|
+
const type = schema.types[baseType];
|
|
2579
|
+
if (type.kind === "type" || type.kind === "input") {
|
|
2580
|
+
const obj = {};
|
|
2581
|
+
for (const fieldName in type.fields || {}) obj[fieldName] = generateSampleValueForType(type.fields[fieldName].type, schema);
|
|
2582
|
+
return obj;
|
|
2583
|
+
}
|
|
2584
|
+
if (type.kind === "enum") return `ENUM_${baseType}`;
|
|
2585
|
+
}
|
|
2586
|
+
return null;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
function generateSampleInput(schema, nodeType, operation) {
|
|
2590
|
+
const returnTypeName = getReturnTypeForOperation(nodeType, operation);
|
|
2591
|
+
if (!returnTypeName || !schema.types[returnTypeName]) return {};
|
|
2592
|
+
const returnType = schema.types[returnTypeName];
|
|
2593
|
+
const result = {};
|
|
2594
|
+
for (const fieldName in returnType.fields || {}) {
|
|
2595
|
+
const field = returnType.fields[fieldName];
|
|
2596
|
+
result[fieldName] = generateSampleValueForType(field.type, schema);
|
|
2597
|
+
}
|
|
2598
|
+
return result;
|
|
2599
|
+
}
|
|
2600
|
+
function MappingEditor({ sourceSchema, targetSchema, sourceNode, sourceOperation, targetNode, targetOperation, className, setEdges, edgeId }) {
|
|
2601
|
+
const { toast } = useToast();
|
|
2602
|
+
const tr = useTr();
|
|
2603
|
+
const [mappingJson, setMappingJson] = useState({});
|
|
2604
|
+
const [jsonDataString, setJsonDataString] = useState(JSON.stringify(mappingJson, null, 2));
|
|
2605
|
+
const [jsonError, setJsonError] = useState(null);
|
|
2606
|
+
const [validationErrors, setValidationErrors] = useState([]);
|
|
2607
|
+
const [sampleInput, setSampleInput] = useState({});
|
|
2608
|
+
const [previewOutput, setPreviewOutput] = useState(null);
|
|
2609
|
+
const [isTestingMapping, setIsTestingMapping] = useState(false);
|
|
2610
|
+
useEffect(() => {
|
|
2611
|
+
const defaultMapping = generateDefaultMapping(sourceSchema, sourceNode, sourceOperation, targetSchema, targetNode, targetOperation);
|
|
2612
|
+
setMappingJson(defaultMapping);
|
|
2613
|
+
setJsonDataString(JSON.stringify(defaultMapping, null, 2));
|
|
2614
|
+
const generatedSampleInput = generateSampleInput(sourceSchema, sourceNode, sourceOperation);
|
|
2615
|
+
setSampleInput(generatedSampleInput);
|
|
2616
|
+
const errors = validateMapping(defaultMapping);
|
|
2617
|
+
setValidationErrors(errors);
|
|
2618
|
+
}, [
|
|
2619
|
+
sourceSchema,
|
|
2620
|
+
targetSchema,
|
|
2621
|
+
sourceNode,
|
|
2622
|
+
sourceOperation,
|
|
2623
|
+
targetNode,
|
|
2624
|
+
targetOperation
|
|
2625
|
+
]);
|
|
2626
|
+
const handleJsonStringChange = (e) => {
|
|
2627
|
+
setJsonDataString(e.target.value);
|
|
2628
|
+
setJsonError(null);
|
|
2629
|
+
};
|
|
2630
|
+
const handleJsonBlur = () => {
|
|
2631
|
+
try {
|
|
2632
|
+
const parsed = JSON.parse(jsonDataString);
|
|
2633
|
+
setMappingJson(parsed);
|
|
2634
|
+
setJsonError(null);
|
|
2635
|
+
const errors = validateMapping(parsed);
|
|
2636
|
+
setValidationErrors(errors);
|
|
2637
|
+
} catch (error) {
|
|
2638
|
+
console.error("JSON parsing error:", error);
|
|
2639
|
+
setJsonError(tr("fluidgrids.builder.mappingEditor.invalidJsonFormat", "Invalid JSON format. Please correct it."));
|
|
2640
|
+
}
|
|
2641
|
+
};
|
|
2642
|
+
const handleMappingChange = (newMapping) => {
|
|
2643
|
+
setMappingJson(newMapping);
|
|
2644
|
+
setJsonDataString(JSON.stringify(newMapping, null, 2));
|
|
2645
|
+
const errors = validateMapping(newMapping);
|
|
2646
|
+
setValidationErrors(errors);
|
|
2647
|
+
if (setEdges && edgeId && errors.length === 0) setEdges((edges) => {
|
|
2648
|
+
return edges.map((edge) => {
|
|
2649
|
+
if (edge.id === edgeId) return {
|
|
2650
|
+
...edge,
|
|
2651
|
+
mapping: {
|
|
2652
|
+
definition: newMapping,
|
|
2653
|
+
comment: `Mapping from ${sourceNode}.${sourceOperation} to ${targetNode}.${targetOperation}`
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2656
|
+
return edge;
|
|
2657
|
+
});
|
|
2658
|
+
});
|
|
2659
|
+
};
|
|
2660
|
+
const checkJsonValidity = () => {
|
|
2661
|
+
setJsonError(null);
|
|
2662
|
+
try {
|
|
2663
|
+
const parsedData = JSON.parse(jsonDataString);
|
|
2664
|
+
setMappingJson(parsedData);
|
|
2665
|
+
const errors = validateMapping(parsedData);
|
|
2666
|
+
if (errors.length > 0) {
|
|
2667
|
+
setValidationErrors(errors);
|
|
2668
|
+
throw new Error(`Validation errors: ${errors.join(", ")}`);
|
|
2669
|
+
}
|
|
2670
|
+
if (setEdges && edgeId) setEdges((edges) => {
|
|
2671
|
+
return edges.map((edge) => {
|
|
2672
|
+
if (edge.id === edgeId) return {
|
|
2673
|
+
...edge,
|
|
2674
|
+
mapping: {
|
|
2675
|
+
definition: parsedData,
|
|
2676
|
+
comment: `Mapping from ${sourceNode}.${sourceOperation} to ${targetNode}.${targetOperation}`
|
|
2677
|
+
}
|
|
2678
|
+
};
|
|
2679
|
+
return edge;
|
|
2680
|
+
});
|
|
2681
|
+
});
|
|
2682
|
+
return parsedData;
|
|
2683
|
+
} catch (error) {
|
|
2684
|
+
if (error instanceof SyntaxError) setJsonError(tr("fluidgrids.builder.mappingEditor.cannotSubmitInvalidJson", "Cannot submit: Mapping data must be valid JSON."));
|
|
2685
|
+
console.error("Submission failed:", error);
|
|
2686
|
+
throw error;
|
|
2687
|
+
}
|
|
2688
|
+
};
|
|
2689
|
+
const handleSaveMapping = () => {
|
|
2690
|
+
try {
|
|
2691
|
+
checkJsonValidity();
|
|
2692
|
+
toast({
|
|
2693
|
+
title: tr("fluidgrids.builder.mappingEditor.mappingSavedTitle", "Mapping saved"),
|
|
2694
|
+
description: tr("fluidgrids.builder.mappingEditor.mappingSavedDescription", "Your mapping configuration has been saved successfully.")
|
|
2695
|
+
});
|
|
2696
|
+
} catch (error) {
|
|
2697
|
+
toast({
|
|
2698
|
+
title: tr("fluidgrids.builder.mappingEditor.errorSavingTitle", "Error saving mapping"),
|
|
2699
|
+
description: error instanceof Error ? error.message : tr("fluidgrids.builder.mappingEditor.errorSavingFallback", "Invalid mapping configuration"),
|
|
2700
|
+
variant: "destructive"
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
};
|
|
2704
|
+
const handleTestMapping = async () => {
|
|
2705
|
+
try {
|
|
2706
|
+
setIsTestingMapping(true);
|
|
2707
|
+
const parsedData = checkJsonValidity();
|
|
2708
|
+
const result = await applyMapping(parsedData, sampleInput);
|
|
2709
|
+
setPreviewOutput(result);
|
|
2710
|
+
toast({
|
|
2711
|
+
title: tr("fluidgrids.builder.mappingEditor.mappingTestedTitle", "Mapping tested"),
|
|
2712
|
+
description: tr("fluidgrids.builder.mappingEditor.mappingTestedDescription", "Test completed successfully.")
|
|
2713
|
+
});
|
|
2714
|
+
} catch (error) {
|
|
2715
|
+
console.error("Test mapping error:", error);
|
|
2716
|
+
toast({
|
|
2717
|
+
title: tr("fluidgrids.builder.mappingEditor.mappingTestFailedTitle", "Mapping test failed"),
|
|
2718
|
+
description: error instanceof Error ? error.message : tr("fluidgrids.builder.mappingEditor.mappingTestFailedFallback", "Error executing mapping"),
|
|
2719
|
+
variant: "destructive"
|
|
2720
|
+
});
|
|
2721
|
+
} finally {
|
|
2722
|
+
setIsTestingMapping(false);
|
|
2723
|
+
}
|
|
2724
|
+
};
|
|
2725
|
+
const detectMappingTypes = (mapping) => {
|
|
2726
|
+
const types = /* @__PURE__ */ new Set();
|
|
2727
|
+
const processValue = (value) => {
|
|
2728
|
+
if (typeof value === "string") {
|
|
2729
|
+
const parseResult = parseTemplate(value);
|
|
2730
|
+
if (parseResult.isTemplate) {
|
|
2731
|
+
const expr = parseResult.expression || "";
|
|
2732
|
+
if (expr.includes(".map(") || expr.includes(".filter(") || expr.includes(".reduce(")) types.add("array");
|
|
2733
|
+
else if (expr.includes("Object.") || expr.startsWith("({")) types.add("object");
|
|
2734
|
+
else if (expr.startsWith("parseInt(") || expr.startsWith("parseFloat(") || expr.startsWith("String(")) types.add("cast");
|
|
2735
|
+
else if (expr.includes("TriggerInput.") || expr.includes("WorkflowState.") || expr.includes("RunContext.")) types.add("context");
|
|
2736
|
+
else if (expr.includes(".toUpperCase()") || expr.includes(".toLowerCase()") || expr.includes(".trim()")) types.add("transform");
|
|
2737
|
+
else if (expr.includes(" + ") || expr.includes(" ? ") || expr.includes("Math.")) types.add("expression");
|
|
2738
|
+
else types.add("direct");
|
|
2739
|
+
}
|
|
2740
|
+
} else if (typeof value === "object" && value !== null) Object.values(value).forEach(processValue);
|
|
2741
|
+
};
|
|
2742
|
+
Object.values(mapping).forEach(processValue);
|
|
2743
|
+
return types;
|
|
2744
|
+
};
|
|
2745
|
+
const usedMappingTypes = detectMappingTypes(mappingJson);
|
|
2746
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2747
|
+
className,
|
|
2748
|
+
children: /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
2749
|
+
/* @__PURE__ */ jsxs(CardTitle, {
|
|
2750
|
+
className: "flex items-center justify-between",
|
|
2751
|
+
children: [/* @__PURE__ */ jsx("span", { children: tr("fluidgrids.builder.mappingEditor.cardTitle", "Mapping Configuration") }), validationErrors.length === 0 ? /* @__PURE__ */ jsxs(Badge, {
|
|
2752
|
+
variant: "outline",
|
|
2753
|
+
className: "text-[var(--color-status-success-text)] border-[var(--color-status-success-border)]",
|
|
2754
|
+
children: [/* @__PURE__ */ jsx(CheckCircle2, { className: "h-3 w-3 mr-1" }), tr("fluidgrids.builder.mappingEditor.badgeValid", "Valid")]
|
|
2755
|
+
}) : /* @__PURE__ */ jsxs(Badge, {
|
|
2756
|
+
variant: "outline",
|
|
2757
|
+
className: "text-[var(--color-status-error-text)] border-[var(--color-status-error-border)]",
|
|
2758
|
+
children: [
|
|
2759
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-3 w-3 mr-1" }),
|
|
2760
|
+
validationErrors.length,
|
|
2761
|
+
" Error",
|
|
2762
|
+
validationErrors.length > 1 ? "s" : ""
|
|
2763
|
+
]
|
|
2764
|
+
})]
|
|
2765
|
+
}),
|
|
2766
|
+
/* @__PURE__ */ jsxs(CardDescription, { children: [
|
|
2767
|
+
"Create a mapping from ",
|
|
2768
|
+
sourceNode,
|
|
2769
|
+
".",
|
|
2770
|
+
sourceOperation,
|
|
2771
|
+
" to ",
|
|
2772
|
+
targetNode,
|
|
2773
|
+
".",
|
|
2774
|
+
targetOperation
|
|
2775
|
+
] }),
|
|
2776
|
+
usedMappingTypes.size > 0 && /* @__PURE__ */ jsx("div", {
|
|
2777
|
+
className: "flex flex-wrap gap-1 mt-2",
|
|
2778
|
+
children: Array.from(usedMappingTypes).map((type) => /* @__PURE__ */ jsx(Badge, {
|
|
2779
|
+
variant: "secondary",
|
|
2780
|
+
className: "text-xs",
|
|
2781
|
+
children: type
|
|
2782
|
+
}, type))
|
|
2783
|
+
})
|
|
2784
|
+
] }), /* @__PURE__ */ jsxs(CardContent, { children: [/* @__PURE__ */ jsxs(Tabs, {
|
|
2785
|
+
defaultValue: "visual",
|
|
2786
|
+
className: "w-full",
|
|
2787
|
+
children: [
|
|
2788
|
+
/* @__PURE__ */ jsxs(TabsList, {
|
|
2789
|
+
className: "mb-4",
|
|
2790
|
+
children: [
|
|
2791
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
2792
|
+
value: "visual",
|
|
2793
|
+
children: tr("fluidgrids.builder.mappingEditor.tabVisualMapper", "Visual Mapper")
|
|
2794
|
+
}),
|
|
2795
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
2796
|
+
value: "code",
|
|
2797
|
+
children: tr("fluidgrids.builder.mappingEditor.tabJsonEditor", "JSON Editor")
|
|
2798
|
+
}),
|
|
2799
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
2800
|
+
value: "preview",
|
|
2801
|
+
children: tr("fluidgrids.builder.mappingEditor.tabPreview", "Preview")
|
|
2802
|
+
})
|
|
2803
|
+
]
|
|
2804
|
+
}),
|
|
2805
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
2806
|
+
value: "visual",
|
|
2807
|
+
children: /* @__PURE__ */ jsx(VisualMapper, {
|
|
2808
|
+
sourceSchema,
|
|
2809
|
+
targetSchema,
|
|
2810
|
+
sourceNode,
|
|
2811
|
+
sourceOperation,
|
|
2812
|
+
targetNode,
|
|
2813
|
+
targetOperation,
|
|
2814
|
+
mapping: mappingJson,
|
|
2815
|
+
onMappingChange: handleMappingChange
|
|
2816
|
+
})
|
|
2817
|
+
}),
|
|
2818
|
+
/* @__PURE__ */ jsxs(TabsContent, {
|
|
2819
|
+
value: "code",
|
|
2820
|
+
children: [/* @__PURE__ */ jsx(CodeEditor, {
|
|
2821
|
+
value: jsonDataString,
|
|
2822
|
+
onBlur: handleJsonBlur,
|
|
2823
|
+
onChange: handleJsonStringChange
|
|
2824
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2825
|
+
className: "mt-2 space-y-1",
|
|
2826
|
+
children: [
|
|
2827
|
+
/* @__PURE__ */ jsxs("p", {
|
|
2828
|
+
className: "text-xs text-muted-foreground",
|
|
2829
|
+
children: [
|
|
2830
|
+
tr("fluidgrids.builder.mappingEditor.templateSyntaxHint", "Use JavaScript template literal syntax:"),
|
|
2831
|
+
" ",
|
|
2832
|
+
/* @__PURE__ */ jsxs("code", {
|
|
2833
|
+
className: "bg-muted px-1 rounded",
|
|
2834
|
+
children: [
|
|
2835
|
+
"$",
|
|
2836
|
+
"{",
|
|
2837
|
+
"output.field",
|
|
2838
|
+
"}"
|
|
2839
|
+
]
|
|
2840
|
+
})
|
|
2841
|
+
]
|
|
2842
|
+
}),
|
|
2843
|
+
jsonError && /* @__PURE__ */ jsx("p", {
|
|
2844
|
+
className: "text-sm text-[var(--color-status-error-text)]",
|
|
2845
|
+
children: jsonError
|
|
2846
|
+
}),
|
|
2847
|
+
validationErrors.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
2848
|
+
className: "p-2 bg-[var(--color-status-error-bg-subtle)] rounded-md",
|
|
2849
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2850
|
+
className: "text-sm font-medium text-[var(--color-status-error-text)]",
|
|
2851
|
+
children: tr("fluidgrids.builder.mappingEditor.validationErrors", "Validation Errors:")
|
|
2852
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
2853
|
+
className: "text-xs text-[var(--color-status-error-text)]/80 mt-1 list-disc list-inside",
|
|
2854
|
+
children: validationErrors.map((error, index) => /* @__PURE__ */ jsx("li", { children: error }, index))
|
|
2855
|
+
})]
|
|
2856
|
+
})
|
|
2857
|
+
]
|
|
2858
|
+
})]
|
|
2859
|
+
}),
|
|
2860
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
2861
|
+
value: "preview",
|
|
2862
|
+
children: /* @__PURE__ */ jsx(MappingPreview, {
|
|
2863
|
+
input: sampleInput,
|
|
2864
|
+
mapping: mappingJson,
|
|
2865
|
+
output: previewOutput
|
|
2866
|
+
})
|
|
2867
|
+
})
|
|
2868
|
+
]
|
|
2869
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2870
|
+
className: "mt-6 flex justify-end space-x-2",
|
|
2871
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2872
|
+
variant: "outline",
|
|
2873
|
+
onClick: handleTestMapping,
|
|
2874
|
+
disabled: isTestingMapping,
|
|
2875
|
+
children: isTestingMapping ? tr("fluidgrids.builder.mappingEditor.testing", "Testing...") : tr("fluidgrids.builder.mappingEditor.testMapping", "Test Mapping")
|
|
2876
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2877
|
+
onClick: handleSaveMapping,
|
|
2878
|
+
children: tr("fluidgrids.builder.mappingEditor.saveMapping", "Save Mapping")
|
|
2879
|
+
})]
|
|
2880
|
+
})] })] })
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
// src/builder/components/lib/schema-parser.ts
|
|
2885
|
+
function parseGraphQLSchema(sdl) {
|
|
2886
|
+
const customSchema = {
|
|
2887
|
+
types: {},
|
|
2888
|
+
queryType: null,
|
|
2889
|
+
mutationType: null,
|
|
2890
|
+
subscriptionType: null
|
|
2891
|
+
};
|
|
2892
|
+
try {
|
|
2893
|
+
const schema = buildSchema(sdl);
|
|
2894
|
+
const queryType = schema.getQueryType();
|
|
2895
|
+
const mutationType = schema.getMutationType();
|
|
2896
|
+
const subscriptionType = schema.getSubscriptionType();
|
|
2897
|
+
if (queryType) customSchema.queryType = queryType.name;
|
|
2898
|
+
if (mutationType) customSchema.mutationType = mutationType.name;
|
|
2899
|
+
if (subscriptionType) customSchema.subscriptionType = subscriptionType.name;
|
|
2900
|
+
const typeMap = schema.getTypeMap();
|
|
2901
|
+
Object.values(typeMap).forEach((type) => {
|
|
2902
|
+
if (type.name.startsWith("__")) return;
|
|
2903
|
+
if (type instanceof GraphQLObjectType) processObjectType(type, customSchema);
|
|
2904
|
+
else if (type instanceof GraphQLInputObjectType) processInputObjectType(type, customSchema);
|
|
2905
|
+
else if (type instanceof GraphQLEnumType) processEnumType(type, customSchema);
|
|
2906
|
+
else if (type instanceof GraphQLInterfaceType) processInterfaceType(type, customSchema);
|
|
2907
|
+
else if (type instanceof GraphQLUnionType) processUnionType(type, customSchema);
|
|
2908
|
+
});
|
|
2909
|
+
return customSchema;
|
|
2910
|
+
} catch (error) {
|
|
2911
|
+
console.error("Error parsing GraphQL schema:", error);
|
|
2912
|
+
return customSchema;
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
function processObjectType(type, customSchema) {
|
|
2916
|
+
const fields = type.getFields();
|
|
2917
|
+
const processedFields = {};
|
|
2918
|
+
Object.values(fields).forEach((field) => {
|
|
2919
|
+
processedFields[field.name] = processField(field);
|
|
2920
|
+
});
|
|
2921
|
+
customSchema.types[type.name] = {
|
|
2922
|
+
name: type.name,
|
|
2923
|
+
kind: "type",
|
|
2924
|
+
fields: processedFields
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
function processInputObjectType(type, customSchema) {
|
|
2928
|
+
const fields = type.getFields();
|
|
2929
|
+
const processedFields = {};
|
|
2930
|
+
Object.values(fields).forEach((field) => {
|
|
2931
|
+
processedFields[field.name] = processInputField(field);
|
|
2932
|
+
});
|
|
2933
|
+
customSchema.types[type.name] = {
|
|
2934
|
+
name: type.name,
|
|
2935
|
+
kind: "input",
|
|
2936
|
+
fields: processedFields,
|
|
2937
|
+
inputFields: processedFields
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
function processEnumType(type, customSchema) {
|
|
2941
|
+
customSchema.types[type.name] = {
|
|
2942
|
+
name: type.name,
|
|
2943
|
+
kind: "enum",
|
|
2944
|
+
fields: {}
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
function processInterfaceType(type, customSchema) {
|
|
2948
|
+
const fields = type.getFields();
|
|
2949
|
+
const processedFields = {};
|
|
2950
|
+
Object.values(fields).forEach((field) => {
|
|
2951
|
+
processedFields[field.name] = processField(field);
|
|
2952
|
+
});
|
|
2953
|
+
customSchema.types[type.name] = {
|
|
2954
|
+
name: type.name,
|
|
2955
|
+
kind: "interface",
|
|
2956
|
+
fields: processedFields
|
|
2957
|
+
};
|
|
2958
|
+
}
|
|
2959
|
+
function processUnionType(type, customSchema) {
|
|
2960
|
+
customSchema.types[type.name] = {
|
|
2961
|
+
name: type.name,
|
|
2962
|
+
kind: "union",
|
|
2963
|
+
fields: {}
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
function processField(field) {
|
|
2967
|
+
const fieldType = field.type;
|
|
2968
|
+
const typeInfo = getTypeInfo(fieldType);
|
|
2969
|
+
const args = field.args.map((arg) => processArgument(arg));
|
|
2970
|
+
return {
|
|
2971
|
+
name: field.name,
|
|
2972
|
+
type: typeInfo.typeName,
|
|
2973
|
+
isNonNull: typeInfo.isNonNull,
|
|
2974
|
+
isList: typeInfo.isList,
|
|
2975
|
+
args
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
function processInputField(field) {
|
|
2979
|
+
const fieldType = field.type;
|
|
2980
|
+
const typeInfo = getTypeInfo(fieldType);
|
|
2981
|
+
return {
|
|
2982
|
+
name: field.name,
|
|
2983
|
+
type: typeInfo.typeName,
|
|
2984
|
+
isNonNull: typeInfo.isNonNull,
|
|
2985
|
+
isList: typeInfo.isList,
|
|
2986
|
+
args: []
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function processArgument(arg) {
|
|
2990
|
+
const argType = arg.type;
|
|
2991
|
+
const typeInfo = getTypeInfo(argType);
|
|
2992
|
+
return {
|
|
2993
|
+
name: arg.name,
|
|
2994
|
+
type: typeInfo.typeName,
|
|
2995
|
+
isNonNull: typeInfo.isNonNull,
|
|
2996
|
+
isList: typeInfo.isList
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
function getTypeInfo(inputType) {
|
|
3000
|
+
let typeName = "";
|
|
3001
|
+
let isNonNull = false;
|
|
3002
|
+
let isList = false;
|
|
3003
|
+
let currentType = inputType;
|
|
3004
|
+
if (currentType instanceof GraphQLNonNull) {
|
|
3005
|
+
isNonNull = true;
|
|
3006
|
+
currentType = currentType.ofType;
|
|
3007
|
+
}
|
|
3008
|
+
if (currentType instanceof GraphQLList) {
|
|
3009
|
+
isList = true;
|
|
3010
|
+
currentType = currentType.ofType;
|
|
3011
|
+
if (currentType instanceof GraphQLNonNull) currentType = currentType.ofType;
|
|
3012
|
+
}
|
|
3013
|
+
if (currentType.name) typeName = currentType.name;
|
|
3014
|
+
else if (currentType.ofType?.name) typeName = currentType.ofType.name;
|
|
3015
|
+
return {
|
|
3016
|
+
typeName,
|
|
3017
|
+
isNonNull,
|
|
3018
|
+
isList
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
// src/builder/components/mapper/config/default-data-config.ts
|
|
3023
|
+
/**
|
|
3024
|
+
* Default data config for the mapper component.
|
|
3025
|
+
*
|
|
3026
|
+
* Note: Node configurations (with schemas) are passed dynamically via props
|
|
3027
|
+
* from the edge component. This default config only contains mapping type
|
|
3028
|
+
* examples and type compatibility rules.
|
|
3029
|
+
*/
|
|
3030
|
+
const defaultDataConfig = {
|
|
3031
|
+
nodes: {},
|
|
3032
|
+
mappingTypes: {
|
|
3033
|
+
direct: {
|
|
3034
|
+
displayName: "Direct Mapping",
|
|
3035
|
+
description: "Map a source field directly to a target field using ${output.field} syntax",
|
|
3036
|
+
color: {
|
|
3037
|
+
compatible: MAPPING_COMPATIBILITY_COLORS.compatible,
|
|
3038
|
+
incompatible: MAPPING_COMPATIBILITY_COLORS.incompatible
|
|
3039
|
+
},
|
|
3040
|
+
examples: [
|
|
3041
|
+
{
|
|
3042
|
+
name: "Simple field access",
|
|
3043
|
+
expression: "${output.fieldName}",
|
|
3044
|
+
description: "Access a direct field from the output"
|
|
3045
|
+
},
|
|
3046
|
+
{
|
|
3047
|
+
name: "Nested field access",
|
|
3048
|
+
expression: "${output.user.email}",
|
|
3049
|
+
description: "Access nested properties"
|
|
3050
|
+
},
|
|
3051
|
+
{
|
|
3052
|
+
name: "Array element access",
|
|
3053
|
+
expression: "${output.items[0].name}",
|
|
3054
|
+
description: "Access specific array elements"
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
name: "With fallback",
|
|
3058
|
+
expression: "${output.user?.email || 'default@example.com'}",
|
|
3059
|
+
description: "Safe access with default value"
|
|
3060
|
+
}
|
|
3061
|
+
]
|
|
3062
|
+
},
|
|
3063
|
+
expression: {
|
|
3064
|
+
displayName: "Expression Mapping",
|
|
3065
|
+
description: "Create a custom JavaScript expression to transform the data",
|
|
3066
|
+
color: MAPPING_TYPE_COLORS.expression,
|
|
3067
|
+
examples: [
|
|
3068
|
+
{
|
|
3069
|
+
name: "String concatenation",
|
|
3070
|
+
expression: "${output.firstName + ' ' + output.lastName}",
|
|
3071
|
+
description: "Combine first and last name"
|
|
3072
|
+
},
|
|
3073
|
+
{
|
|
3074
|
+
name: "Template literal string",
|
|
3075
|
+
expression: "${`Hello ${output.name}!`}",
|
|
3076
|
+
description: "Use nested template literals for formatting"
|
|
3077
|
+
},
|
|
3078
|
+
{
|
|
3079
|
+
name: "Mathematical operation",
|
|
3080
|
+
expression: "${output.price * (1 - output.discount)}",
|
|
3081
|
+
description: "Calculate discounted price"
|
|
3082
|
+
},
|
|
3083
|
+
{
|
|
3084
|
+
name: "Conditional logic (ternary)",
|
|
3085
|
+
expression: "${output.age >= 18 ? 'Adult' : 'Minor'}",
|
|
3086
|
+
description: "Determine if adult or minor"
|
|
3087
|
+
},
|
|
3088
|
+
{
|
|
3089
|
+
name: "Nullish coalescing",
|
|
3090
|
+
expression: "${output.value ?? 'default'}",
|
|
3091
|
+
description: "Use default if null/undefined"
|
|
3092
|
+
},
|
|
3093
|
+
{
|
|
3094
|
+
name: "Logical OR fallback",
|
|
3095
|
+
expression: "${output.error || 'Success'}",
|
|
3096
|
+
description: "Fallback for falsy values"
|
|
3097
|
+
},
|
|
3098
|
+
{
|
|
3099
|
+
name: "Array length",
|
|
3100
|
+
expression: "${output.items?.length || 0}",
|
|
3101
|
+
description: "Get the length of an array safely"
|
|
3102
|
+
},
|
|
3103
|
+
{
|
|
3104
|
+
name: "Min value",
|
|
3105
|
+
expression: "${Math.min(...output.values)}",
|
|
3106
|
+
description: "Get the minimum value in an array"
|
|
3107
|
+
},
|
|
3108
|
+
{
|
|
3109
|
+
name: "Max value",
|
|
3110
|
+
expression: "${Math.max(...output.values)}",
|
|
3111
|
+
description: "Get the maximum value in an array"
|
|
3112
|
+
},
|
|
3113
|
+
{
|
|
3114
|
+
name: "Round number",
|
|
3115
|
+
expression: "${Math.round(output.value)}",
|
|
3116
|
+
description: "Round to nearest integer"
|
|
3117
|
+
},
|
|
3118
|
+
{
|
|
3119
|
+
name: "JSON parsing",
|
|
3120
|
+
expression: "${JSON.parse(output.jsonString)}",
|
|
3121
|
+
description: "Parse a JSON string to an object"
|
|
3122
|
+
},
|
|
3123
|
+
{
|
|
3124
|
+
name: "JSON stringify",
|
|
3125
|
+
expression: "${JSON.stringify(output.data)}",
|
|
3126
|
+
description: "Convert object to JSON string"
|
|
3127
|
+
},
|
|
3128
|
+
{
|
|
3129
|
+
name: "Current timestamp",
|
|
3130
|
+
expression: "${new Date().toISOString()}",
|
|
3131
|
+
description: "Get current date/time in ISO format"
|
|
3132
|
+
},
|
|
3133
|
+
{
|
|
3134
|
+
name: "Object creation",
|
|
3135
|
+
expression: "${({ id: output.user.id, email: output.user.email })}",
|
|
3136
|
+
description: "Create a new object from fields"
|
|
3137
|
+
}
|
|
3138
|
+
],
|
|
3139
|
+
safeBuiltins: [
|
|
3140
|
+
"parseInt",
|
|
3141
|
+
"parseFloat",
|
|
3142
|
+
"isNaN",
|
|
3143
|
+
"isFinite",
|
|
3144
|
+
"Number",
|
|
3145
|
+
"String",
|
|
3146
|
+
"Boolean",
|
|
3147
|
+
"Math.min",
|
|
3148
|
+
"Math.max",
|
|
3149
|
+
"Math.round",
|
|
3150
|
+
"Math.floor",
|
|
3151
|
+
"Math.ceil",
|
|
3152
|
+
"Math.abs",
|
|
3153
|
+
"Array.isArray",
|
|
3154
|
+
"JSON.parse",
|
|
3155
|
+
"JSON.stringify",
|
|
3156
|
+
"Object.keys",
|
|
3157
|
+
"Object.values",
|
|
3158
|
+
"Object.entries",
|
|
3159
|
+
"Object.assign",
|
|
3160
|
+
"Date",
|
|
3161
|
+
"new Date()"
|
|
3162
|
+
]
|
|
3163
|
+
},
|
|
3164
|
+
transform: {
|
|
3165
|
+
displayName: "Transform Mapping",
|
|
3166
|
+
description: "Apply string and data transformation functions",
|
|
3167
|
+
color: MAPPING_TYPE_COLORS.transform,
|
|
3168
|
+
examples: [
|
|
3169
|
+
{
|
|
3170
|
+
name: "Uppercase",
|
|
3171
|
+
expression: "${output.field.toUpperCase()}",
|
|
3172
|
+
description: "Convert text to uppercase"
|
|
3173
|
+
},
|
|
3174
|
+
{
|
|
3175
|
+
name: "Lowercase",
|
|
3176
|
+
expression: "${output.field.toLowerCase()}",
|
|
3177
|
+
description: "Convert text to lowercase"
|
|
3178
|
+
},
|
|
3179
|
+
{
|
|
3180
|
+
name: "Trim whitespace",
|
|
3181
|
+
expression: "${output.field.trim()}",
|
|
3182
|
+
description: "Remove leading/trailing whitespace"
|
|
3183
|
+
},
|
|
3184
|
+
{
|
|
3185
|
+
name: "Split string",
|
|
3186
|
+
expression: "${output.field.split(',')}",
|
|
3187
|
+
description: "Split string into array by delimiter"
|
|
3188
|
+
},
|
|
3189
|
+
{
|
|
3190
|
+
name: "Join array",
|
|
3191
|
+
expression: "${output.items.join(', ')}",
|
|
3192
|
+
description: "Join array elements with delimiter"
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
name: "Replace text",
|
|
3196
|
+
expression: "${output.field.replace(/old/g, 'new')}",
|
|
3197
|
+
description: "Replace all occurrences of text"
|
|
3198
|
+
},
|
|
3199
|
+
{
|
|
3200
|
+
name: "Substring",
|
|
3201
|
+
expression: "${output.field.substring(0, 10)}",
|
|
3202
|
+
description: "Extract part of a string"
|
|
3203
|
+
},
|
|
3204
|
+
{
|
|
3205
|
+
name: "Slice",
|
|
3206
|
+
expression: "${output.field.slice(-5)}",
|
|
3207
|
+
description: "Get last 5 characters"
|
|
3208
|
+
},
|
|
3209
|
+
{
|
|
3210
|
+
name: "Includes check",
|
|
3211
|
+
expression: "${output.field.includes('search')}",
|
|
3212
|
+
description: "Check if string contains text"
|
|
3213
|
+
},
|
|
3214
|
+
{
|
|
3215
|
+
name: "Starts with",
|
|
3216
|
+
expression: "${output.field.startsWith('prefix')}",
|
|
3217
|
+
description: "Check if string starts with text"
|
|
3218
|
+
}
|
|
3219
|
+
],
|
|
3220
|
+
functions: [
|
|
3221
|
+
{
|
|
3222
|
+
name: "toUpperCase",
|
|
3223
|
+
displayName: "Uppercase",
|
|
3224
|
+
description: "Convert text to uppercase",
|
|
3225
|
+
example: "${output.field.toUpperCase()}"
|
|
3226
|
+
},
|
|
3227
|
+
{
|
|
3228
|
+
name: "toLowerCase",
|
|
3229
|
+
displayName: "Lowercase",
|
|
3230
|
+
description: "Convert text to lowercase",
|
|
3231
|
+
example: "${output.field.toLowerCase()}"
|
|
3232
|
+
},
|
|
3233
|
+
{
|
|
3234
|
+
name: "trim",
|
|
3235
|
+
displayName: "Trim",
|
|
3236
|
+
description: "Remove whitespace from start and end",
|
|
3237
|
+
example: "${output.field.trim()}"
|
|
3238
|
+
},
|
|
3239
|
+
{
|
|
3240
|
+
name: "split",
|
|
3241
|
+
displayName: "Split",
|
|
3242
|
+
description: "Split string into array by delimiter",
|
|
3243
|
+
example: "${output.field.split(',')}"
|
|
3244
|
+
},
|
|
3245
|
+
{
|
|
3246
|
+
name: "join",
|
|
3247
|
+
displayName: "Join",
|
|
3248
|
+
description: "Join array elements with delimiter",
|
|
3249
|
+
example: "${output.items.join('-')}"
|
|
3250
|
+
},
|
|
3251
|
+
{
|
|
3252
|
+
name: "replace",
|
|
3253
|
+
displayName: "Replace",
|
|
3254
|
+
description: "Replace text in string",
|
|
3255
|
+
example: "${output.field.replace('old', 'new')}"
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
name: "concat",
|
|
3259
|
+
displayName: "Concatenate",
|
|
3260
|
+
description: "Join multiple strings together",
|
|
3261
|
+
example: "${output.first + ' ' + output.last}"
|
|
3262
|
+
}
|
|
3263
|
+
]
|
|
3264
|
+
},
|
|
3265
|
+
cast: {
|
|
3266
|
+
displayName: "Cast Mapping",
|
|
3267
|
+
description: "Convert data from one type to another using JavaScript functions",
|
|
3268
|
+
color: MAPPING_TYPE_COLORS.cast,
|
|
3269
|
+
examples: [
|
|
3270
|
+
{
|
|
3271
|
+
name: "String to Integer",
|
|
3272
|
+
expression: "${parseInt(output.numberString)}",
|
|
3273
|
+
description: "Convert string to integer"
|
|
3274
|
+
},
|
|
3275
|
+
{
|
|
3276
|
+
name: "String to Float",
|
|
3277
|
+
expression: "${parseFloat(output.decimalString)}",
|
|
3278
|
+
description: "Convert string to decimal number"
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
name: "To String",
|
|
3282
|
+
expression: "${String(output.value)}",
|
|
3283
|
+
description: "Convert any value to string"
|
|
3284
|
+
},
|
|
3285
|
+
{
|
|
3286
|
+
name: "To Boolean",
|
|
3287
|
+
expression: "${Boolean(output.value)}",
|
|
3288
|
+
description: "Convert to true/false"
|
|
3289
|
+
},
|
|
3290
|
+
{
|
|
3291
|
+
name: "Number with default",
|
|
3292
|
+
expression: "${Number(output.value) || 0}",
|
|
3293
|
+
description: "Convert to number with fallback"
|
|
3294
|
+
},
|
|
3295
|
+
{
|
|
3296
|
+
name: "Parse with radix",
|
|
3297
|
+
expression: "${parseInt(output.hex, 16)}",
|
|
3298
|
+
description: "Parse hexadecimal string"
|
|
3299
|
+
},
|
|
3300
|
+
{
|
|
3301
|
+
name: "Fixed decimals",
|
|
3302
|
+
expression: "${parseFloat(output.price).toFixed(2)}",
|
|
3303
|
+
description: "Format number with 2 decimal places"
|
|
3304
|
+
}
|
|
3305
|
+
],
|
|
3306
|
+
types: [
|
|
3307
|
+
{
|
|
3308
|
+
name: "parseInt",
|
|
3309
|
+
displayName: "Integer",
|
|
3310
|
+
description: "Convert to whole number",
|
|
3311
|
+
example: "${parseInt(output.field)}"
|
|
3312
|
+
},
|
|
3313
|
+
{
|
|
3314
|
+
name: "parseFloat",
|
|
3315
|
+
displayName: "Float",
|
|
3316
|
+
description: "Convert to decimal number",
|
|
3317
|
+
example: "${parseFloat(output.field)}"
|
|
3318
|
+
},
|
|
3319
|
+
{
|
|
3320
|
+
name: "String",
|
|
3321
|
+
displayName: "String",
|
|
3322
|
+
description: "Convert to text",
|
|
3323
|
+
example: "${String(output.field)}"
|
|
3324
|
+
},
|
|
3325
|
+
{
|
|
3326
|
+
name: "Boolean",
|
|
3327
|
+
displayName: "Boolean",
|
|
3328
|
+
description: "Convert to true/false",
|
|
3329
|
+
example: "${Boolean(output.field)}"
|
|
3330
|
+
},
|
|
3331
|
+
{
|
|
3332
|
+
name: "Number",
|
|
3333
|
+
displayName: "Number",
|
|
3334
|
+
description: "Convert to number",
|
|
3335
|
+
example: "${Number(output.field)}"
|
|
3336
|
+
}
|
|
3337
|
+
]
|
|
3338
|
+
},
|
|
3339
|
+
array: {
|
|
3340
|
+
displayName: "Array Mapping",
|
|
3341
|
+
description: "Transform arrays using map, filter, reduce and other array methods",
|
|
3342
|
+
color: MAPPING_TYPE_COLORS.array,
|
|
3343
|
+
examples: [
|
|
3344
|
+
{
|
|
3345
|
+
name: "Map - Extract IDs",
|
|
3346
|
+
expression: "${output.users.map(u => u.id)}",
|
|
3347
|
+
description: "Extract a single field from each item"
|
|
3348
|
+
},
|
|
3349
|
+
{
|
|
3350
|
+
name: "Map - Transform objects",
|
|
3351
|
+
expression: "${output.items.map(item => ({ name: item.name, value: item.value }))}",
|
|
3352
|
+
description: "Transform each item to new shape"
|
|
3353
|
+
},
|
|
3354
|
+
{
|
|
3355
|
+
name: "Map - With index",
|
|
3356
|
+
expression: "${output.items.map((item, index) => ({ ...item, position: index + 1 }))}",
|
|
3357
|
+
description: "Include index in transformation"
|
|
3358
|
+
},
|
|
3359
|
+
{
|
|
3360
|
+
name: "Filter - Active users",
|
|
3361
|
+
expression: "${output.users.filter(u => u.active)}",
|
|
3362
|
+
description: "Keep only items matching condition"
|
|
3363
|
+
},
|
|
3364
|
+
{
|
|
3365
|
+
name: "Filter - By value",
|
|
3366
|
+
expression: "${output.items.filter(item => item.price > 100)}",
|
|
3367
|
+
description: "Filter by numeric comparison"
|
|
3368
|
+
},
|
|
3369
|
+
{
|
|
3370
|
+
name: "Filter + Map",
|
|
3371
|
+
expression: "${output.users.filter(u => u.active).map(u => u.id)}",
|
|
3372
|
+
description: "Chain filter and map operations"
|
|
3373
|
+
},
|
|
3374
|
+
{
|
|
3375
|
+
name: "Reduce - Sum",
|
|
3376
|
+
expression: "${output.items.reduce((sum, item) => sum + item.price, 0)}",
|
|
3377
|
+
description: "Calculate total of all prices"
|
|
3378
|
+
},
|
|
3379
|
+
{
|
|
3380
|
+
name: "Reduce - Average",
|
|
3381
|
+
expression: "${output.items.reduce((sum, item) => sum + item.value, 0) / output.items.length}",
|
|
3382
|
+
description: "Calculate average value"
|
|
3383
|
+
},
|
|
3384
|
+
{
|
|
3385
|
+
name: "Reduce - Group by",
|
|
3386
|
+
expression: "${output.items.reduce((acc, item) => ({ ...acc, [item.category]: [...(acc[item.category] || []), item] }), {})}",
|
|
3387
|
+
description: "Group items by category"
|
|
3388
|
+
},
|
|
3389
|
+
{
|
|
3390
|
+
name: "Find - First match",
|
|
3391
|
+
expression: "${output.items.find(item => item.id === TriggerInput.itemId)}",
|
|
3392
|
+
description: "Find first item matching condition"
|
|
3393
|
+
},
|
|
3394
|
+
{
|
|
3395
|
+
name: "Some - Any match",
|
|
3396
|
+
expression: "${output.items.some(item => item.active)}",
|
|
3397
|
+
description: "Check if any item matches"
|
|
3398
|
+
},
|
|
3399
|
+
{
|
|
3400
|
+
name: "Every - All match",
|
|
3401
|
+
expression: "${output.items.every(item => item.valid)}",
|
|
3402
|
+
description: "Check if all items match"
|
|
3403
|
+
},
|
|
3404
|
+
{
|
|
3405
|
+
name: "Flat - Flatten array",
|
|
3406
|
+
expression: "${output.nestedArrays.flat()}",
|
|
3407
|
+
description: "Flatten one level of nesting"
|
|
3408
|
+
},
|
|
3409
|
+
{
|
|
3410
|
+
name: "FlatMap - Map and flatten",
|
|
3411
|
+
expression: "${output.users.flatMap(u => u.permissions)}",
|
|
3412
|
+
description: "Map then flatten results"
|
|
3413
|
+
},
|
|
3414
|
+
{
|
|
3415
|
+
name: "Sort - Ascending",
|
|
3416
|
+
expression: "${[...output.items].sort((a, b) => a.price - b.price)}",
|
|
3417
|
+
description: "Sort items by price ascending"
|
|
3418
|
+
},
|
|
3419
|
+
{
|
|
3420
|
+
name: "Sort - Descending",
|
|
3421
|
+
expression: "${[...output.items].sort((a, b) => b.price - a.price)}",
|
|
3422
|
+
description: "Sort items by price descending"
|
|
3423
|
+
},
|
|
3424
|
+
{
|
|
3425
|
+
name: "Slice - First N",
|
|
3426
|
+
expression: "${output.items.slice(0, 5)}",
|
|
3427
|
+
description: "Get first 5 items"
|
|
3428
|
+
},
|
|
3429
|
+
{
|
|
3430
|
+
name: "Slice - Last N",
|
|
3431
|
+
expression: "${output.items.slice(-3)}",
|
|
3432
|
+
description: "Get last 3 items"
|
|
3433
|
+
},
|
|
3434
|
+
{
|
|
3435
|
+
name: "Unique values",
|
|
3436
|
+
expression: "${[...new Set(output.items.map(i => i.category))]}",
|
|
3437
|
+
description: "Get unique categories"
|
|
3438
|
+
},
|
|
3439
|
+
{
|
|
3440
|
+
name: "Concat arrays",
|
|
3441
|
+
expression: "${output.list1.concat(output.list2)}",
|
|
3442
|
+
description: "Combine two arrays"
|
|
3443
|
+
},
|
|
3444
|
+
{
|
|
3445
|
+
name: "Includes check",
|
|
3446
|
+
expression: "${output.tags.includes('important')}",
|
|
3447
|
+
description: "Check if array contains value"
|
|
3448
|
+
},
|
|
3449
|
+
{
|
|
3450
|
+
name: "Index of",
|
|
3451
|
+
expression: "${output.items.findIndex(i => i.id === 'target')}",
|
|
3452
|
+
description: "Find index of matching item"
|
|
3453
|
+
}
|
|
3454
|
+
]
|
|
3455
|
+
},
|
|
3456
|
+
object: {
|
|
3457
|
+
displayName: "Object Mapping",
|
|
3458
|
+
description: "Transform and manipulate objects",
|
|
3459
|
+
color: MAPPING_TYPE_COLORS.object,
|
|
3460
|
+
examples: [
|
|
3461
|
+
{
|
|
3462
|
+
name: "Get keys",
|
|
3463
|
+
expression: "${Object.keys(output.metadata)}",
|
|
3464
|
+
description: "Get all property names as array"
|
|
3465
|
+
},
|
|
3466
|
+
{
|
|
3467
|
+
name: "Get values",
|
|
3468
|
+
expression: "${Object.values(output.metadata)}",
|
|
3469
|
+
description: "Get all property values as array"
|
|
3470
|
+
},
|
|
3471
|
+
{
|
|
3472
|
+
name: "Get entries",
|
|
3473
|
+
expression: "${Object.entries(output.metadata)}",
|
|
3474
|
+
description: "Get key-value pairs as array"
|
|
3475
|
+
},
|
|
3476
|
+
{
|
|
3477
|
+
name: "Merge objects",
|
|
3478
|
+
expression: "${Object.assign({}, output.user, output.preferences)}",
|
|
3479
|
+
description: "Combine multiple objects"
|
|
3480
|
+
},
|
|
3481
|
+
{
|
|
3482
|
+
name: "Spread merge",
|
|
3483
|
+
expression: "${{ ...output.defaults, ...output.overrides }}",
|
|
3484
|
+
description: "Merge with spread operator"
|
|
3485
|
+
},
|
|
3486
|
+
{
|
|
3487
|
+
name: "Pick fields",
|
|
3488
|
+
expression: "${({ id: output.user.id, email: output.user.email })}",
|
|
3489
|
+
description: "Select specific fields only"
|
|
3490
|
+
},
|
|
3491
|
+
{
|
|
3492
|
+
name: "From entries",
|
|
3493
|
+
expression: "${Object.fromEntries(output.pairs)}",
|
|
3494
|
+
description: "Create object from key-value pairs"
|
|
3495
|
+
},
|
|
3496
|
+
{
|
|
3497
|
+
name: "Has property",
|
|
3498
|
+
expression: "${'email' in output.user}",
|
|
3499
|
+
description: "Check if property exists"
|
|
3500
|
+
}
|
|
3501
|
+
]
|
|
3502
|
+
},
|
|
3503
|
+
context: {
|
|
3504
|
+
displayName: "Workflow Context",
|
|
3505
|
+
description: "Access workflow execution context and state from previous nodes",
|
|
3506
|
+
color: MAPPING_TYPE_COLORS.context,
|
|
3507
|
+
examples: [
|
|
3508
|
+
{
|
|
3509
|
+
name: "Trigger input",
|
|
3510
|
+
expression: "${TriggerInput.userId}",
|
|
3511
|
+
description: "Access original trigger payload"
|
|
3512
|
+
},
|
|
3513
|
+
{
|
|
3514
|
+
name: "Trigger nested data",
|
|
3515
|
+
expression: "${TriggerInput.webhookData.eventType}",
|
|
3516
|
+
description: "Access nested trigger data"
|
|
3517
|
+
},
|
|
3518
|
+
{
|
|
3519
|
+
name: "Previous node output",
|
|
3520
|
+
expression: "${WorkflowState.fetch_user.output.name}",
|
|
3521
|
+
description: "Access output from a specific previous node"
|
|
3522
|
+
},
|
|
3523
|
+
{
|
|
3524
|
+
name: "Node status",
|
|
3525
|
+
expression: "${WorkflowState.validate_data.status}",
|
|
3526
|
+
description: "Check status of a previous node"
|
|
3527
|
+
},
|
|
3528
|
+
{
|
|
3529
|
+
name: "Combine node outputs",
|
|
3530
|
+
expression: "${WorkflowState.step1.output.count + WorkflowState.step2.output.count}",
|
|
3531
|
+
description: "Combine data from multiple nodes"
|
|
3532
|
+
},
|
|
3533
|
+
{
|
|
3534
|
+
name: "Run ID",
|
|
3535
|
+
expression: "${RunContext.runId}",
|
|
3536
|
+
description: "Current workflow run identifier"
|
|
3537
|
+
},
|
|
3538
|
+
{
|
|
3539
|
+
name: "Workflow key",
|
|
3540
|
+
expression: "${RunContext.workflowKey}",
|
|
3541
|
+
description: "Workflow definition identifier"
|
|
3542
|
+
},
|
|
3543
|
+
{
|
|
3544
|
+
name: "Tenant ID",
|
|
3545
|
+
expression: "${RunContext.tenantId}",
|
|
3546
|
+
description: "Current tenant identifier"
|
|
3547
|
+
},
|
|
3548
|
+
{
|
|
3549
|
+
name: "Workspace ID",
|
|
3550
|
+
expression: "${RunContext.workspaceId}",
|
|
3551
|
+
description: "Current workspace identifier"
|
|
3552
|
+
},
|
|
3553
|
+
{
|
|
3554
|
+
name: "Triggered at",
|
|
3555
|
+
expression: "${RunContext.triggeredAt}",
|
|
3556
|
+
description: "Workflow trigger timestamp"
|
|
3557
|
+
},
|
|
3558
|
+
{
|
|
3559
|
+
name: "Triggered by",
|
|
3560
|
+
expression: "${RunContext.triggeredBy}",
|
|
3561
|
+
description: "User who triggered the workflow"
|
|
3562
|
+
}
|
|
3563
|
+
]
|
|
3564
|
+
}
|
|
3565
|
+
},
|
|
3566
|
+
typeCompatibility: {
|
|
3567
|
+
String: ["ID", "String"],
|
|
3568
|
+
ID: ["String", "ID"],
|
|
3569
|
+
Int: [
|
|
3570
|
+
"Float",
|
|
3571
|
+
"Int",
|
|
3572
|
+
"String"
|
|
3573
|
+
],
|
|
3574
|
+
Float: ["String", "Float"],
|
|
3575
|
+
Boolean: ["Boolean", "String"]
|
|
3576
|
+
},
|
|
3577
|
+
sampleData: {
|
|
3578
|
+
search: { response: [{
|
|
3579
|
+
metadata: {
|
|
3580
|
+
title: "Introduction to Machine Learning",
|
|
3581
|
+
source: "https://example.com/ml-intro",
|
|
3582
|
+
sourceLabel: "Example ML Guide",
|
|
3583
|
+
sourceType: "webpage",
|
|
3584
|
+
sourceInfo: "Educational resource",
|
|
3585
|
+
partition: "default",
|
|
3586
|
+
score: .95,
|
|
3587
|
+
images: ["https://example.com/ml-image.jpg"]
|
|
3588
|
+
},
|
|
3589
|
+
content: "Machine learning is a branch of artificial intelligence..."
|
|
3590
|
+
}, {
|
|
3591
|
+
metadata: {
|
|
3592
|
+
title: "Deep Learning Fundamentals",
|
|
3593
|
+
source: "https://example.com/dl-basics",
|
|
3594
|
+
sourceLabel: "Deep Learning Guide",
|
|
3595
|
+
sourceType: "webpage",
|
|
3596
|
+
sourceInfo: "Technical documentation",
|
|
3597
|
+
partition: "default",
|
|
3598
|
+
score: .87,
|
|
3599
|
+
images: ["https://example.com/dl-image.jpg"]
|
|
3600
|
+
},
|
|
3601
|
+
content: "Deep learning is a subset of machine learning..."
|
|
3602
|
+
}] },
|
|
3603
|
+
insert: {
|
|
3604
|
+
success: true,
|
|
3605
|
+
count: 1
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
};
|
|
3609
|
+
|
|
3610
|
+
// src/builder/components/mapper/app.tsx
|
|
3611
|
+
function convertInlineSchemaToGraphQL(inlineSchema, nodeType) {
|
|
3612
|
+
const schema = {
|
|
3613
|
+
types: {},
|
|
3614
|
+
queryType: "Query",
|
|
3615
|
+
mutationType: null,
|
|
3616
|
+
subscriptionType: null
|
|
3617
|
+
};
|
|
3618
|
+
if (inlineSchema.outputFields && inlineSchema.outputFields.length > 0) {
|
|
3619
|
+
const outputFields = {};
|
|
3620
|
+
inlineSchema.outputFields.forEach((field) => {
|
|
3621
|
+
outputFields[field.name] = {
|
|
3622
|
+
name: field.name,
|
|
3623
|
+
type: field.type,
|
|
3624
|
+
isNonNull: field.required || false,
|
|
3625
|
+
isList: field.type.startsWith("["),
|
|
3626
|
+
args: []
|
|
3627
|
+
};
|
|
3628
|
+
});
|
|
3629
|
+
const outputTypeName = `${nodeType}Output`;
|
|
3630
|
+
schema.types[outputTypeName] = {
|
|
3631
|
+
name: outputTypeName,
|
|
3632
|
+
kind: "type",
|
|
3633
|
+
fields: outputFields
|
|
3634
|
+
};
|
|
3635
|
+
schema.types["Query"] = {
|
|
3636
|
+
name: "Query",
|
|
3637
|
+
kind: "type",
|
|
3638
|
+
fields: { execute: {
|
|
3639
|
+
name: "execute",
|
|
3640
|
+
type: outputTypeName,
|
|
3641
|
+
isNonNull: false,
|
|
3642
|
+
isList: false,
|
|
3643
|
+
args: []
|
|
3644
|
+
} }
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
if (inlineSchema.inputFields && inlineSchema.inputFields.length > 0) {
|
|
3648
|
+
const inputFields = {};
|
|
3649
|
+
inlineSchema.inputFields.forEach((field) => {
|
|
3650
|
+
inputFields[field.name] = {
|
|
3651
|
+
name: field.name,
|
|
3652
|
+
type: field.type,
|
|
3653
|
+
isNonNull: field.required || false,
|
|
3654
|
+
isList: field.type.startsWith("["),
|
|
3655
|
+
args: []
|
|
3656
|
+
};
|
|
3657
|
+
});
|
|
3658
|
+
const inputTypeName = `${nodeType}Input`;
|
|
3659
|
+
schema.types[inputTypeName] = {
|
|
3660
|
+
name: inputTypeName,
|
|
3661
|
+
kind: "input",
|
|
3662
|
+
fields: {},
|
|
3663
|
+
inputFields
|
|
3664
|
+
};
|
|
3665
|
+
}
|
|
3666
|
+
return schema;
|
|
3667
|
+
}
|
|
3668
|
+
function App({ dataConfig = defaultDataConfig, sourceSchema: propSourceSchema, targetSchema: propTargetSchema, sourceOperation, targetOperation, sourceNode, targetNode, setEdges, edgeId }) {
|
|
3669
|
+
const tr = useTr();
|
|
3670
|
+
const initialParsedSchemas = useMemo(() => {
|
|
3671
|
+
const parsed = {};
|
|
3672
|
+
initializeConfig(parsed, dataConfig);
|
|
3673
|
+
return parsed;
|
|
3674
|
+
}, [dataConfig]);
|
|
3675
|
+
const [parsedSchemas, setParsedSchemas] = useState(initialParsedSchemas);
|
|
3676
|
+
const configInitialized = parsedSchemas === initialParsedSchemas || Object.keys(parsedSchemas).length > 0;
|
|
3677
|
+
const [selectedSourceNode, _setSelectedSourceNode] = useState(sourceNode ?? null);
|
|
3678
|
+
const [selectedSourceOperation, _setSelectedSourceOperation] = useState(sourceOperation ?? null);
|
|
3679
|
+
const [selectedTargetNode, _setSelectedTargetNode] = useState(targetNode ?? null);
|
|
3680
|
+
const [selectedTargetOperation, _setSelectedTargetOperation] = useState(targetOperation ?? null);
|
|
3681
|
+
useEffect(() => {
|
|
3682
|
+
if (configInitialized) {
|
|
3683
|
+
const initializeSchemas = () => {
|
|
3684
|
+
const newParsedSchemas = {};
|
|
3685
|
+
if (propSourceSchema && selectedSourceNode) newParsedSchemas[selectedSourceNode] = propSourceSchema;
|
|
3686
|
+
if (propTargetSchema && selectedTargetNode) newParsedSchemas[selectedTargetNode] = propTargetSchema;
|
|
3687
|
+
Object.keys(dataConfig.nodes).forEach((nodeKey) => {
|
|
3688
|
+
if (!newParsedSchemas[nodeKey]) {
|
|
3689
|
+
const nodeInfo = dataConfig.nodes[nodeKey];
|
|
3690
|
+
if (nodeInfo?.schema && typeof nodeInfo.schema !== "string" && (nodeInfo.schema.inputFields || nodeInfo.schema.outputFields)) newParsedSchemas[nodeKey] = convertInlineSchemaToGraphQL(nodeInfo.schema, nodeKey);
|
|
3691
|
+
else newParsedSchemas[nodeKey] = {
|
|
3692
|
+
types: {},
|
|
3693
|
+
queryType: null,
|
|
3694
|
+
mutationType: null,
|
|
3695
|
+
subscriptionType: null
|
|
3696
|
+
};
|
|
3697
|
+
}
|
|
3698
|
+
});
|
|
3699
|
+
setParsedSchemas((prev) => {
|
|
3700
|
+
return {
|
|
3701
|
+
...prev,
|
|
3702
|
+
...newParsedSchemas
|
|
3703
|
+
};
|
|
3704
|
+
});
|
|
3705
|
+
initializeConfig(newParsedSchemas, dataConfig);
|
|
3706
|
+
};
|
|
3707
|
+
initializeSchemas();
|
|
3708
|
+
}
|
|
3709
|
+
}, [
|
|
3710
|
+
configInitialized,
|
|
3711
|
+
dataConfig,
|
|
3712
|
+
propSourceSchema,
|
|
3713
|
+
propTargetSchema,
|
|
3714
|
+
selectedSourceNode,
|
|
3715
|
+
selectedTargetNode
|
|
3716
|
+
]);
|
|
3717
|
+
const getSchemaForNode = (nodeType, isSource) => {
|
|
3718
|
+
if (!nodeType) return null;
|
|
3719
|
+
if (isSource && propSourceSchema) return propSourceSchema;
|
|
3720
|
+
if (!isSource && propTargetSchema) return propTargetSchema;
|
|
3721
|
+
if (parsedSchemas[nodeType]) return parsedSchemas[nodeType];
|
|
3722
|
+
const nodeInfo = dataConfig.nodes[nodeType];
|
|
3723
|
+
if (nodeInfo) {
|
|
3724
|
+
if (nodeInfo.schema && typeof nodeInfo.schema !== "string" && (nodeInfo.schema.inputFields || nodeInfo.schema.outputFields)) {
|
|
3725
|
+
const convertedSchema = convertInlineSchemaToGraphQL(nodeInfo.schema, nodeType);
|
|
3726
|
+
setParsedSchemas((prev) => ({
|
|
3727
|
+
...prev,
|
|
3728
|
+
[nodeType]: convertedSchema
|
|
3729
|
+
}));
|
|
3730
|
+
return convertedSchema;
|
|
3731
|
+
}
|
|
3732
|
+
if (typeof nodeInfo.schema === "string") {
|
|
3733
|
+
const parsedSchema = parseGraphQLSchema(nodeInfo.schema);
|
|
3734
|
+
setParsedSchemas((prev) => ({
|
|
3735
|
+
...prev,
|
|
3736
|
+
[nodeType]: parsedSchema
|
|
3737
|
+
}));
|
|
3738
|
+
return parsedSchema;
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
console.warn(`Could not find or parse schema for node type: ${nodeType}`);
|
|
3742
|
+
return null;
|
|
3743
|
+
};
|
|
3744
|
+
const currentSourceSchema = propSourceSchema || getSchemaForNode(selectedSourceNode, true);
|
|
3745
|
+
const currentTargetSchema = propTargetSchema || getSchemaForNode(selectedTargetNode, false);
|
|
3746
|
+
return /* @__PURE__ */ jsx(DataConfigProvider, {
|
|
3747
|
+
config: dataConfig,
|
|
3748
|
+
children: /* @__PURE__ */ jsxs(ThemeProvider$1, {
|
|
3749
|
+
defaultTheme: "light",
|
|
3750
|
+
storageKey: "vite-ui-theme",
|
|
3751
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3752
|
+
className: "min-h-screen bg-background",
|
|
3753
|
+
children: /* @__PURE__ */ jsxs("main", {
|
|
3754
|
+
className: "container py-6",
|
|
3755
|
+
children: [currentSourceSchema && currentTargetSchema && selectedSourceNode && selectedSourceOperation && selectedTargetNode && selectedTargetOperation && /* @__PURE__ */ jsxs("div", {
|
|
3756
|
+
className: "mt-8 p-4 border rounded-lg",
|
|
3757
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
3758
|
+
className: "text-lg font-medium mb-4",
|
|
3759
|
+
children: tr("fluidgrids.builder.mapperApp.dataMappingHeading", "Data Mapping")
|
|
3760
|
+
}), /* @__PURE__ */ jsx(MappingEditor, {
|
|
3761
|
+
sourceSchema: currentSourceSchema,
|
|
3762
|
+
targetSchema: currentTargetSchema,
|
|
3763
|
+
sourceNode: selectedSourceNode,
|
|
3764
|
+
sourceOperation: selectedSourceOperation,
|
|
3765
|
+
targetNode: selectedTargetNode,
|
|
3766
|
+
targetOperation: selectedTargetOperation,
|
|
3767
|
+
setEdges,
|
|
3768
|
+
edgeId
|
|
3769
|
+
})]
|
|
3770
|
+
}), process.env.NODE_ENV === "development" && /* @__PURE__ */ jsxs("div", {
|
|
3771
|
+
className: "mt-8 p-4 border border-default rounded-lg bg-muted",
|
|
3772
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
3773
|
+
className: "text-md font-medium mb-2",
|
|
3774
|
+
children: "Debug Information"
|
|
3775
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
3776
|
+
className: "text-sm",
|
|
3777
|
+
children: [
|
|
3778
|
+
/* @__PURE__ */ jsxs("p", { children: ["Source Node: ", selectedSourceNode || "None"] }),
|
|
3779
|
+
/* @__PURE__ */ jsxs("p", { children: ["Source Operation: ", selectedSourceOperation || "None"] }),
|
|
3780
|
+
/* @__PURE__ */ jsxs("p", { children: ["Target Node: ", selectedTargetNode || "None"] }),
|
|
3781
|
+
/* @__PURE__ */ jsxs("p", { children: ["Target Operation: ", selectedTargetOperation || "None"] }),
|
|
3782
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
3783
|
+
"Source Schema:",
|
|
3784
|
+
" ",
|
|
3785
|
+
currentSourceSchema ? "Available" : "Not available"
|
|
3786
|
+
] }),
|
|
3787
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
3788
|
+
"Target Schema:",
|
|
3789
|
+
" ",
|
|
3790
|
+
currentTargetSchema ? "Available" : "Not available"
|
|
3791
|
+
] }),
|
|
3792
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
3793
|
+
"Parsed Schemas:",
|
|
3794
|
+
" ",
|
|
3795
|
+
Object.keys(parsedSchemas).join(", ") || "None"
|
|
3796
|
+
] }),
|
|
3797
|
+
/* @__PURE__ */ jsxs("details", { children: [/* @__PURE__ */ jsx("summary", { children: "Schema Details" }), /* @__PURE__ */ jsx("pre", {
|
|
3798
|
+
className: "text-xs mt-2 overflow-auto max-h-[200px]",
|
|
3799
|
+
children: JSON.stringify({
|
|
3800
|
+
sourceSchema: currentSourceSchema ? Object.keys(currentSourceSchema.types).length + " types" : "Not available",
|
|
3801
|
+
targetSchema: currentTargetSchema ? Object.keys(currentTargetSchema.types).length + " types" : "Not available"
|
|
3802
|
+
}, null, 2)
|
|
3803
|
+
})] })
|
|
3804
|
+
]
|
|
3805
|
+
})]
|
|
3806
|
+
})]
|
|
3807
|
+
})
|
|
3808
|
+
}), /* @__PURE__ */ jsx(Toaster, {})]
|
|
3809
|
+
})
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
export { App as default };
|