data-primals-engine 1.6.0 → 1.6.2-rc1
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/README.md +968 -924
- package/client/package-lock.json +524 -5
- package/client/package.json +2 -1
- package/client/src/App.scss +14 -1
- package/client/src/Dashboard.jsx +4 -0
- package/client/src/DataLayout.jsx +85 -20
- package/client/src/DataLayout.scss +32 -6
- package/client/src/DataTable.jsx +19 -12
- package/client/src/ModelCreator.jsx +12 -12
- package/client/src/ModelCreator.scss +1 -1
- package/client/src/ModelImporter.jsx +4 -1
- package/client/src/ViewSwitcher.jsx +1 -1
- package/client/src/WorkflowEditor.jsx +317 -0
- package/client/src/WorkflowEditor.scss +16 -0
- package/package.json +142 -142
- package/src/defaultModels.js +12 -2
- package/src/i18n.js +35 -3
- package/src/index.js +1 -0
- package/src/modules/data/data.operations.js +91 -5
- package/src/modules/user.js +14 -9
- package/src/modules/workflow.js +1 -4
- package/src/packs.js +239 -24
- package/test/data.integration.test.js +75 -0
- package/test/user.test.js +106 -1
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import ReactFlow, {
|
|
3
|
+
MiniMap,
|
|
4
|
+
Controls,
|
|
5
|
+
Background,
|
|
6
|
+
useNodesState,
|
|
7
|
+
useEdgesState,
|
|
8
|
+
addEdge
|
|
9
|
+
} from 'reactflow';
|
|
10
|
+
import 'reactflow/dist/style.css';
|
|
11
|
+
import { useModelContext } from './contexts/ModelContext';
|
|
12
|
+
import { useAuthContext } from './contexts/AuthContext';
|
|
13
|
+
import { Trans, useTranslation } from 'react-i18next';
|
|
14
|
+
import { CodeField } from "./Field.jsx";
|
|
15
|
+
import { useQuery } from 'react-query';
|
|
16
|
+
|
|
17
|
+
import "./WorkflowEditor.scss"
|
|
18
|
+
// --- Custom Node Types (à créer) ---
|
|
19
|
+
// import TriggerNode from './nodes/TriggerNode';
|
|
20
|
+
// import StepNode from './nodes/StepNode';
|
|
21
|
+
|
|
22
|
+
const nodeTypes = {
|
|
23
|
+
// trigger: TriggerNode,
|
|
24
|
+
// step: StepNode,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const WorkflowEditor = ({ workflowId }) => {
|
|
28
|
+
const { t } = useTranslation();
|
|
29
|
+
const { me } = useAuthContext();
|
|
30
|
+
const { models } = useModelContext();
|
|
31
|
+
|
|
32
|
+
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
|
33
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
|
34
|
+
const [selectedNode, setSelectedNode] = useState(null);
|
|
35
|
+
const [panelWidth, setPanelWidth] = useState(300);
|
|
36
|
+
const resizeData = useRef({ isResizing: false, initialX: 0, initialWidth: 0 });
|
|
37
|
+
|
|
38
|
+
const handleMouseDown = (e) => {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
resizeData.current = {
|
|
41
|
+
isResizing: true,
|
|
42
|
+
initialX: e.clientX,
|
|
43
|
+
initialWidth: panelWidth
|
|
44
|
+
};
|
|
45
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
46
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const handleMouseMove = useCallback((e) => {
|
|
50
|
+
if (!resizeData.current.isResizing) return; // Utilise la référence qui ne change pas
|
|
51
|
+
const dx = e.clientX - resizeData.current.initialX; // Calcule le delta depuis le clic initial
|
|
52
|
+
const newWidth = resizeData.current.initialWidth - dx; // Applique le delta à la largeur initiale
|
|
53
|
+
setPanelWidth(Math.max(200, Math.min(newWidth, 800))); // Applique les limites
|
|
54
|
+
}, []); // Plus besoin de dépendances, la logique est entièrement contenue dans la ref
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
const handleMouseUp = () => {
|
|
58
|
+
resizeData.current.isResizing = false;
|
|
59
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
60
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// Query 1: Fetch the main workflow document
|
|
65
|
+
const { data: mainWorkflowData, isLoading: isLoadingMainWorkflow, error: errorMainWorkflow } = useQuery(
|
|
66
|
+
['mainWorkflow', workflowId],
|
|
67
|
+
() => fetch('/api/data/search?_user='+me.username, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
model: 'workflow',
|
|
72
|
+
filter: { $eq: [{$toString:'$_id'}, workflowId] },
|
|
73
|
+
// No pipelines, no depth here, as per user request
|
|
74
|
+
})
|
|
75
|
+
}).then(res => res.json()),
|
|
76
|
+
{
|
|
77
|
+
enabled: !!workflowId,
|
|
78
|
+
select: (data) => data?.data?.[0]
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Query 2: Fetch all workflowStep documents related to this workflow
|
|
83
|
+
const { data: workflowStepsData, isLoading: isLoadingSteps, error: errorSteps } = useQuery(
|
|
84
|
+
['workflowSteps', workflowId],
|
|
85
|
+
() => fetch('/api/data/search?_user=' + me.username, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
model: 'workflowStep',
|
|
90
|
+
filter: { workflow: workflowId },
|
|
91
|
+
// Use a shallow depth to populate direct relations like 'actions'
|
|
92
|
+
// This avoids a deep recursive lookup for the entire graph, but makes step data usable
|
|
93
|
+
depth: 1
|
|
94
|
+
})
|
|
95
|
+
}).then(res => res.json()),
|
|
96
|
+
{
|
|
97
|
+
enabled: !!workflowId, // La requête ne s'exécute que si workflowId est défini
|
|
98
|
+
select: (data) => data?.data || []
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Query 3: Fetch all workflowAction documents related to this workflow
|
|
103
|
+
const { data: workflowActionsData, isLoading: isLoadingActions, error: errorActions } = useQuery(
|
|
104
|
+
['workflowActions', workflowId],
|
|
105
|
+
() => fetch('/api/data/search?_user=' + me.username, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: { 'Content-Type': 'application/json' },
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
model: 'workflowAction',
|
|
110
|
+
filter: { workflow: workflowId },
|
|
111
|
+
// No depth needed here as actions are usually flat
|
|
112
|
+
})
|
|
113
|
+
}).then(res => res.json()),
|
|
114
|
+
{
|
|
115
|
+
enabled: !!workflowId,
|
|
116
|
+
select: (data) => data?.data || []
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (mainWorkflowData && workflowStepsData && workflowActionsData) {
|
|
122
|
+
const initialNodes = [];
|
|
123
|
+
const initialEdges = [];
|
|
124
|
+
const processedSteps = new Set();
|
|
125
|
+
|
|
126
|
+
// Create a map for quick lookup of steps
|
|
127
|
+
const stepsMap = new Map(workflowStepsData.map(step => [step._id, step]));
|
|
128
|
+
|
|
129
|
+
// 1. Nœud du déclencheur (Trigger)
|
|
130
|
+
initialNodes.push({
|
|
131
|
+
id: `workflow-start-${mainWorkflowData._id}`,
|
|
132
|
+
type: 'input',
|
|
133
|
+
position: { x: 250, y: 5 },
|
|
134
|
+
data: {
|
|
135
|
+
label: `Début: ${typeof mainWorkflowData.name === 'object' ? mainWorkflowData.name.value : mainWorkflowData.name}`,
|
|
136
|
+
// --- CORRECTION ---
|
|
137
|
+
// On inclut toutes les données du workflow pour la cohérence
|
|
138
|
+
...mainWorkflowData
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Create a map for quick lookup of actions
|
|
143
|
+
const actionsMap = new Map(workflowActionsData.map(action => [action._id, action]));
|
|
144
|
+
|
|
145
|
+
// 2. Traitement récursif des étapes
|
|
146
|
+
const processStep = (stepId, parentNodeId, parentPosition, isSuccessBranch = true) => {
|
|
147
|
+
if (!stepId || processedSteps.has(stepId)) return;
|
|
148
|
+
|
|
149
|
+
const step = stepsMap.get(stepId);
|
|
150
|
+
if (!step) {
|
|
151
|
+
console.warn(`WorkflowEditor: Step with ID ${stepId} not found in fetched steps.`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
processedSteps.add(stepId);
|
|
156
|
+
|
|
157
|
+
const nodePosition = { x: parentPosition.x, y: parentPosition.y + 120 };
|
|
158
|
+
initialNodes.push({
|
|
159
|
+
id: step._id,
|
|
160
|
+
type: 'default',
|
|
161
|
+
className: 'workflow-step-node',
|
|
162
|
+
position: nodePosition,
|
|
163
|
+
data: {
|
|
164
|
+
label: (step?.name && typeof step.name === 'object' ? step.name.value : step.name) || `Étape ${step._id}`,
|
|
165
|
+
...step // --- CORRECTION --- On inclut l'objet step complet
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
initialEdges.push({ id: `e-${parentNodeId}-${step._id}`, source: parentNodeId, target: step._id, type: 'smoothstep' });
|
|
169
|
+
|
|
170
|
+
let lastActionId = step._id;
|
|
171
|
+
// The actions on the step are just IDs now, we need to look them up
|
|
172
|
+
const actionIds = Array.isArray(step.actions) ? step.actions : (step.actions ? [step.actions] : []);
|
|
173
|
+
const actions = actionIds.map(id => actionsMap.get(id)).filter(Boolean); // Get full action objects
|
|
174
|
+
|
|
175
|
+
if (actionIds.length !== actions.length) {
|
|
176
|
+
const missingIds = actionIds.filter(id => !actionsMap.has(id));
|
|
177
|
+
console.warn(`WorkflowEditor: Could not find the following actions: ${missingIds.join(', ')}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
actions.forEach((action, index) => {
|
|
181
|
+
const actionNodePosition = { x: nodePosition.x, y: nodePosition.y + (index + 1) * 80 };
|
|
182
|
+
initialNodes.push({
|
|
183
|
+
id: action._id,
|
|
184
|
+
type: 'default',
|
|
185
|
+
className: 'workflow-action-node',
|
|
186
|
+
position: actionNodePosition,
|
|
187
|
+
data: {
|
|
188
|
+
label: action.name.value || `Action ${action._id}`,
|
|
189
|
+
...action // --- CORRECTION --- On inclut l'objet action complet
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
initialEdges.push({ id: `e-${lastActionId}-${action._id}`, source: lastActionId, target: action._id, type: 'smoothstep' });
|
|
193
|
+
lastActionId = action._id;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Créer les liens (edges)
|
|
197
|
+
if (step.onSuccessStep && typeof step.onSuccessStep === 'string') { // onSuccessStep is an ID
|
|
198
|
+
processStep(step.onSuccessStep, lastActionId, { x: nodePosition.x - 200, y: nodePosition.y + (actions.length * 80) }, true);
|
|
199
|
+
initialEdges.push({ id: `e-${lastActionId}-success-${step.onSuccessStep}`, source: lastActionId, target: step.onSuccessStep, label: t('onSuccess', 'onSuccess'), type: 'smoothstep' });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (step.onFailureStep && typeof step.onFailureStep === 'string') { // onFailureStep is an ID
|
|
203
|
+
processStep(step.onFailureStep, lastActionId, { x: nodePosition.x + 200, y: nodePosition.y + (actions.length * 80) }, false);
|
|
204
|
+
initialEdges.push({ id: `e-${lastActionId}-failure-${step.onFailureStep}`, source: lastActionId, target: step.onFailureStep, label: t('onFailure', 'onFailure'), type: 'smoothstep', animated: true, style: { stroke: '#ff4d4d' } });
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
if (mainWorkflowData.startStep) {
|
|
209
|
+
initialEdges.push({
|
|
210
|
+
id: `e-start-${mainWorkflowData.startStep}`, // startStep is an ID
|
|
211
|
+
source: `workflow-start-${mainWorkflowData._id}`,
|
|
212
|
+
target: mainWorkflowData.startStep,
|
|
213
|
+
type: 'smoothstep',
|
|
214
|
+
});
|
|
215
|
+
processStep(mainWorkflowData.startStep, `workflow-start-${mainWorkflowData._id}`, { x: 250, y: 5 });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
setNodes(initialNodes);
|
|
219
|
+
setEdges(initialEdges);
|
|
220
|
+
}
|
|
221
|
+
}, [mainWorkflowData, workflowStepsData, workflowActionsData, setNodes, setEdges, t]);
|
|
222
|
+
|
|
223
|
+
const onConnect = useCallback(
|
|
224
|
+
(params) => setEdges((eds) => addEdge(params, eds)),
|
|
225
|
+
[setEdges],
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const onNodeClick = useCallback((event, node) => { // Keep this for potential future node editing
|
|
229
|
+
setSelectedNode(node);
|
|
230
|
+
console.log('Node clicked:', node);
|
|
231
|
+
}, []);
|
|
232
|
+
|
|
233
|
+
const onPaneClick = useCallback(() => { // Keep this for potential future node editing
|
|
234
|
+
setSelectedNode(null);
|
|
235
|
+
}, []);
|
|
236
|
+
|
|
237
|
+
if (isLoadingMainWorkflow || isLoadingSteps || isLoadingActions) { // Check all loading states
|
|
238
|
+
return <div>{t('loading', 'Chargement...')}</div>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (errorMainWorkflow || errorSteps || errorActions) { // Check all error states
|
|
242
|
+
const errorMessage = errorMainWorkflow?.message || errorSteps?.message || errorActions?.message || 'Unknown error';
|
|
243
|
+
return <div><Trans i18nKey="error.generic" values={{ message: errorMessage }}>Erreur: {{ message: errorMessage }}</Trans></div>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<div className={"flex-1"} style={{ width: '100%', height: '80vh', display: 'flex' }}>
|
|
248
|
+
<ReactFlow
|
|
249
|
+
nodes={nodes}
|
|
250
|
+
edges={edges}
|
|
251
|
+
onNodesChange={onNodesChange}
|
|
252
|
+
onEdgesChange={onEdgesChange}
|
|
253
|
+
onConnect={onConnect}
|
|
254
|
+
onNodeClick={onNodeClick}
|
|
255
|
+
onPaneClick={onPaneClick}
|
|
256
|
+
nodeTypes={nodeTypes}
|
|
257
|
+
fitView
|
|
258
|
+
>
|
|
259
|
+
<Controls />
|
|
260
|
+
<MiniMap />
|
|
261
|
+
<Background variant="dots" gap={12} size={1} />
|
|
262
|
+
</ReactFlow>
|
|
263
|
+
|
|
264
|
+
{/* Panneau latéral pour l'édition des propriétés */}
|
|
265
|
+
{selectedNode && (
|
|
266
|
+
<div className="properties-panel" style={{ width: `${panelWidth}px`, padding: '10px', borderLeft: '1px solid #ccc' }}>
|
|
267
|
+
<div
|
|
268
|
+
className="resizer"
|
|
269
|
+
onMouseDown={handleMouseDown}
|
|
270
|
+
/>
|
|
271
|
+
<h3><Trans i18nKey="properties">Propriétés</Trans></h3>
|
|
272
|
+
<p><strong>ID:</strong> {selectedNode.id}</p>
|
|
273
|
+
|
|
274
|
+
{/* Affiche les conditions pour un workflowStep */}
|
|
275
|
+
{selectedNode.data.conditions && (
|
|
276
|
+
<div className="mt-2">
|
|
277
|
+
<strong><Trans i18nKey="conditions">Conditions:</Trans></strong>
|
|
278
|
+
<CodeField language="json" value={JSON.stringify(selectedNode.data.conditions, null, 2)} readOnly={true} />
|
|
279
|
+
</div>
|
|
280
|
+
)}
|
|
281
|
+
|
|
282
|
+
{/* Affiche le script pour une action ExecuteScript */}
|
|
283
|
+
{selectedNode.data.type && (
|
|
284
|
+
<div className="mt-2">
|
|
285
|
+
<strong><Trans i18nKey="action">Action :</Trans></strong> {selectedNode.data.type}
|
|
286
|
+
{selectedNode.data.targetModel&& (
|
|
287
|
+
<p>
|
|
288
|
+
<strong><Trans i18nKey="action">Modèle cible : </Trans></strong>{selectedNode.data.targetModel}
|
|
289
|
+
</p>
|
|
290
|
+
)}
|
|
291
|
+
{selectedNode.data.targetSelector&& (
|
|
292
|
+
<p>
|
|
293
|
+
<strong><Trans i18nKey="filter">Filtre sur :</Trans></strong>
|
|
294
|
+
<br/><CodeField language="javascript" value={JSON.stringify(selectedNode.data.targetSelector)} readOnly={true} />
|
|
295
|
+
</p>
|
|
296
|
+
)}
|
|
297
|
+
{(selectedNode.data.dataToCreate || selectedNode.data.fieldsToUpdate) && (
|
|
298
|
+
<p>
|
|
299
|
+
<strong><Trans i18nKey="filter">Code déclenché :</Trans></strong>
|
|
300
|
+
<br/><CodeField language="javascript" value={JSON.stringify(selectedNode.data.dataToCreate || selectedNode.data.fieldsToUpdate)} readOnly={true} />
|
|
301
|
+
</p>
|
|
302
|
+
)}
|
|
303
|
+
</div>
|
|
304
|
+
)}
|
|
305
|
+
{/* Affiche le script pour une action ExecuteScript */}
|
|
306
|
+
{selectedNode.data.type === 'ExecuteScript' && selectedNode.data.script && (
|
|
307
|
+
<div className="mt-2">
|
|
308
|
+
<CodeField language="javascript" value={selectedNode.data.script} readOnly={true} />
|
|
309
|
+
</div>
|
|
310
|
+
)}
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export default WorkflowEditor;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
.workflow-step-node {
|
|
3
|
+
background-color: #e0e7ff; /* Un bleu clair pour les étapes */
|
|
4
|
+
border-color: #a5b4fc;
|
|
5
|
+
border-width: 2px;
|
|
6
|
+
font-weight: bold;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.workflow-action-node {
|
|
10
|
+
background-color: #f0f9ff; /* Un bleu très clair pour les actions */
|
|
11
|
+
border-color: #bae6fd;
|
|
12
|
+
border-width: 1px;
|
|
13
|
+
border-style: dashed;
|
|
14
|
+
width: auto;
|
|
15
|
+
min-width: 150px;
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.6.
|
|
4
|
-
"description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
|
|
5
|
-
"main": "src/engine.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"preinstall": "npx force-resolutions",
|
|
9
|
-
"dev": "concurrently \"npm:client\" \"npm:devserver\"",
|
|
10
|
-
"prod": "npm run build-server && npm run server",
|
|
11
|
-
"client": "cd client && npm run dev",
|
|
12
|
-
"build-server": "vite build client --config client/vite.config.js --outDir dist",
|
|
13
|
-
"build-client": "cd client && npm run build",
|
|
14
|
-
"server": "cross-env NODE_ENV=production node server.js",
|
|
15
|
-
"devserver": "npm run build-server && cross-env NODE_ENV=development PORT=7633 node server.js",
|
|
16
|
-
"lint": "eslint .",
|
|
17
|
-
"format": "prettier --write .",
|
|
18
|
-
"clean": "rm -rf node_modules package-lock.json",
|
|
19
|
-
"test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism --no-watch",
|
|
20
|
-
"audit": "npm audit --audit-level=high",
|
|
21
|
-
"migrate:create": "node src/migrate.js create",
|
|
22
|
-
"migrate:up": "node src/migrate.js up",
|
|
23
|
-
"migrate:down": "node src/migrate.js down",
|
|
24
|
-
"migrate:revert": "node src/migrate.js revert",
|
|
25
|
-
"migrate:status": "node src/migrate.js status"
|
|
26
|
-
},
|
|
27
|
-
"optionalDependencies": {
|
|
28
|
-
"@rollup/rollup-linux-x64-gnu": "4.6.1"
|
|
29
|
-
},
|
|
30
|
-
"resolutions": {
|
|
31
|
-
"on-headers": "1.1.0",
|
|
32
|
-
"brace-expansion": "2.0.2",
|
|
33
|
-
"prismjs": "1.30.0",
|
|
34
|
-
"xml2js": ">=0.5.0",
|
|
35
|
-
"tar-fs": ">=3.1.1",
|
|
36
|
-
"vite": ">=7.0.8"
|
|
37
|
-
},
|
|
38
|
-
"overrides": {
|
|
39
|
-
"react-syntax-highlighter": {
|
|
40
|
-
"prismjs": "1.30.0"
|
|
41
|
-
},
|
|
42
|
-
"react-code-blocks": {
|
|
43
|
-
"prismjs": "1.30.0"
|
|
44
|
-
},
|
|
45
|
-
"refractor": {
|
|
46
|
-
"prismjs": "1.30.0"
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
"repository": {
|
|
50
|
-
"type": "git",
|
|
51
|
-
"url": "https://github.com/anonympins/data-primals-engine.git"
|
|
52
|
-
},
|
|
53
|
-
"exports": {
|
|
54
|
-
".": "./src/index.js",
|
|
55
|
-
"./modules/*.js": "./src/modules/*.js",
|
|
56
|
-
"./modules/*": "./src/modules/*/index.js",
|
|
57
|
-
"./client": "./client/index.js",
|
|
58
|
-
"./*": "./src/*.js"
|
|
59
|
-
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@langchain/core": "^0.3.
|
|
62
|
-
"archiver": "^7.0.1",
|
|
63
|
-
"aws-sdk": "^2.1692.0",
|
|
64
|
-
"bcrypt": "^6.0.0",
|
|
65
|
-
"body-parser": "^2.2.0",
|
|
66
|
-
"chalk": "^5.4.1",
|
|
67
|
-
"check-disk-space": "^3.4.0",
|
|
68
|
-
"compression": "^1.8.1",
|
|
69
|
-
"cookie-parser": "^1.4.7",
|
|
70
|
-
"cronstrue": "^3.2.0",
|
|
71
|
-
"csv-parse": "^6.1.0",
|
|
72
|
-
"date-fns": "^4.1.0",
|
|
73
|
-
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
74
|
-
"express-formidable": "^1.2.0",
|
|
75
|
-
"express-mongo-sanitize": "^2.2.0",
|
|
76
|
-
"express-rate-limit": "^8.0.1",
|
|
77
|
-
"express-session": "^1.18.2",
|
|
78
|
-
"handlebars": "^4.7.8",
|
|
79
|
-
"i18next-browser-languagedetector": "^8.2.0",
|
|
80
|
-
"isolated-vm": "^5.0.0",
|
|
81
|
-
"juice": "^11.0.
|
|
82
|
-
"mathjs": "^
|
|
83
|
-
"mongodb": "^6.18.0",
|
|
84
|
-
"node-cache": "^5.1.2",
|
|
85
|
-
"node-schedule": "^2.1.1",
|
|
86
|
-
"nodemailer": "^7.0.
|
|
87
|
-
"openai": "^
|
|
88
|
-
"passport": "^0.7.0",
|
|
89
|
-
"passport-saml-encrypted": "^0.1.13",
|
|
90
|
-
"process": "^0.11.10",
|
|
91
|
-
"prop-types": "^15.8.1",
|
|
92
|
-
"randomcolor": "^0.6.2",
|
|
93
|
-
"react-markdown": "^10.1.0",
|
|
94
|
-
"read-excel-file": "^
|
|
95
|
-
"request-ip": "^3.3.0",
|
|
96
|
-
"safe-regex": "^2.1.1",
|
|
97
|
-
"sanitize-html": "^2.17.0",
|
|
98
|
-
"sirv": "^3.0.
|
|
99
|
-
"stripe": "^
|
|
100
|
-
"swagger-ui-express": "^5.0.1",
|
|
101
|
-
"tar": "^7.
|
|
102
|
-
"tinycolor2": "^1.6.0",
|
|
103
|
-
"uniqid": "^5.4.0",
|
|
104
|
-
"vitest": "^3.2.4",
|
|
105
|
-
"yaml": "^2.8.
|
|
106
|
-
},
|
|
107
|
-
"peerDependencies": {
|
|
108
|
-
"express": "^5.1.0",
|
|
109
|
-
"passport-azure-ad": "^4.3.5",
|
|
110
|
-
"passport-google-oauth20": "^2.0.0",
|
|
111
|
-
"react": "18.3.1",
|
|
112
|
-
"react-i18next": "^
|
|
113
|
-
"react-query": "
|
|
114
|
-
"@langchain/anthropic": "^0.3.
|
|
115
|
-
"@langchain/deepseek": "^0.1.0",
|
|
116
|
-
"@langchain/google-genai": "^0.2.
|
|
117
|
-
"@langchain/openai": "^0.6.
|
|
118
|
-
},
|
|
119
|
-
"devDependencies": {
|
|
120
|
-
"@eslint/js": "^9.32.0",
|
|
121
|
-
"concurrently": "^9.2.0",
|
|
122
|
-
"cross-env": "^10.0.0",
|
|
123
|
-
"eslint": "^9.32.0",
|
|
124
|
-
"globals": "^16.3.0"
|
|
125
|
-
},
|
|
126
|
-
"subPackages": [
|
|
127
|
-
"client"
|
|
128
|
-
],
|
|
129
|
-
"keywords": [
|
|
130
|
-
"data-driven engine",
|
|
131
|
-
"headless CMS",
|
|
132
|
-
"backend",
|
|
133
|
-
"automation",
|
|
134
|
-
"AWS S3",
|
|
135
|
-
"MongoDB"
|
|
136
|
-
],
|
|
137
|
-
"author": "anonympins",
|
|
138
|
-
"license": "MIT",
|
|
139
|
-
"engines": {
|
|
140
|
-
"node": ">=20.0.0"
|
|
141
|
-
}
|
|
142
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "data-primals-engine",
|
|
3
|
+
"version": "1.6.2-rc1",
|
|
4
|
+
"description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
|
|
5
|
+
"main": "src/engine.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"preinstall": "npx force-resolutions",
|
|
9
|
+
"dev": "concurrently \"npm:client\" \"npm:devserver\"",
|
|
10
|
+
"prod": "npm run build-server && npm run server",
|
|
11
|
+
"client": "cd client && npm run dev",
|
|
12
|
+
"build-server": "vite build client --config client/vite.config.js --outDir dist",
|
|
13
|
+
"build-client": "cd client && npm run build",
|
|
14
|
+
"server": "cross-env NODE_ENV=production node server.js",
|
|
15
|
+
"devserver": "npm run build-server && cross-env NODE_ENV=development PORT=7633 node server.js",
|
|
16
|
+
"lint": "eslint .",
|
|
17
|
+
"format": "prettier --write .",
|
|
18
|
+
"clean": "rm -rf node_modules package-lock.json",
|
|
19
|
+
"test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism --no-watch",
|
|
20
|
+
"audit": "npm audit --audit-level=high",
|
|
21
|
+
"migrate:create": "node src/migrate.js create",
|
|
22
|
+
"migrate:up": "node src/migrate.js up",
|
|
23
|
+
"migrate:down": "node src/migrate.js down",
|
|
24
|
+
"migrate:revert": "node src/migrate.js revert",
|
|
25
|
+
"migrate:status": "node src/migrate.js status"
|
|
26
|
+
},
|
|
27
|
+
"optionalDependencies": {
|
|
28
|
+
"@rollup/rollup-linux-x64-gnu": "4.6.1"
|
|
29
|
+
},
|
|
30
|
+
"resolutions": {
|
|
31
|
+
"on-headers": "1.1.0",
|
|
32
|
+
"brace-expansion": "2.0.2",
|
|
33
|
+
"prismjs": "1.30.0",
|
|
34
|
+
"xml2js": ">=0.5.0",
|
|
35
|
+
"tar-fs": ">=3.1.1",
|
|
36
|
+
"vite": ">=7.0.8"
|
|
37
|
+
},
|
|
38
|
+
"overrides": {
|
|
39
|
+
"react-syntax-highlighter": {
|
|
40
|
+
"prismjs": "1.30.0"
|
|
41
|
+
},
|
|
42
|
+
"react-code-blocks": {
|
|
43
|
+
"prismjs": "1.30.0"
|
|
44
|
+
},
|
|
45
|
+
"refractor": {
|
|
46
|
+
"prismjs": "1.30.0"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/anonympins/data-primals-engine.git"
|
|
52
|
+
},
|
|
53
|
+
"exports": {
|
|
54
|
+
".": "./src/index.js",
|
|
55
|
+
"./modules/*.js": "./src/modules/*.js",
|
|
56
|
+
"./modules/*": "./src/modules/*/index.js",
|
|
57
|
+
"./client": "./client/index.js",
|
|
58
|
+
"./*": "./src/*.js"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@langchain/core": "^0.3.79",
|
|
62
|
+
"archiver": "^7.0.1",
|
|
63
|
+
"aws-sdk": "^2.1692.0",
|
|
64
|
+
"bcrypt": "^6.0.0",
|
|
65
|
+
"body-parser": "^2.2.0",
|
|
66
|
+
"chalk": "^5.4.1",
|
|
67
|
+
"check-disk-space": "^3.4.0",
|
|
68
|
+
"compression": "^1.8.1",
|
|
69
|
+
"cookie-parser": "^1.4.7",
|
|
70
|
+
"cronstrue": "^3.2.0",
|
|
71
|
+
"csv-parse": "^6.1.0",
|
|
72
|
+
"date-fns": "^4.1.0",
|
|
73
|
+
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
74
|
+
"express-formidable": "^1.2.0",
|
|
75
|
+
"express-mongo-sanitize": "^2.2.0",
|
|
76
|
+
"express-rate-limit": "^8.0.1",
|
|
77
|
+
"express-session": "^1.18.2",
|
|
78
|
+
"handlebars": "^4.7.8",
|
|
79
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
80
|
+
"isolated-vm": "^5.0.0",
|
|
81
|
+
"juice": "^11.0.3",
|
|
82
|
+
"mathjs": "^15.1.0",
|
|
83
|
+
"mongodb": "^6.18.0",
|
|
84
|
+
"node-cache": "^5.1.2",
|
|
85
|
+
"node-schedule": "^2.1.1",
|
|
86
|
+
"nodemailer": "^7.0.10",
|
|
87
|
+
"openai": "^6.9.1",
|
|
88
|
+
"passport": "^0.7.0",
|
|
89
|
+
"passport-saml-encrypted": "^0.1.13",
|
|
90
|
+
"process": "^0.11.10",
|
|
91
|
+
"prop-types": "^15.8.1",
|
|
92
|
+
"randomcolor": "^0.6.2",
|
|
93
|
+
"react-markdown": "^10.1.0",
|
|
94
|
+
"read-excel-file": "^6.0.1",
|
|
95
|
+
"request-ip": "^3.3.0",
|
|
96
|
+
"safe-regex": "^2.1.1",
|
|
97
|
+
"sanitize-html": "^2.17.0",
|
|
98
|
+
"sirv": "^3.0.2",
|
|
99
|
+
"stripe": "^20.0.0",
|
|
100
|
+
"swagger-ui-express": "^5.0.1",
|
|
101
|
+
"tar": "^7.5.2",
|
|
102
|
+
"tinycolor2": "^1.6.0",
|
|
103
|
+
"uniqid": "^5.4.0",
|
|
104
|
+
"vitest": "^3.2.4",
|
|
105
|
+
"yaml": "^2.8.1"
|
|
106
|
+
},
|
|
107
|
+
"peerDependencies": {
|
|
108
|
+
"express": "^5.1.0",
|
|
109
|
+
"passport-azure-ad": "^4.3.5",
|
|
110
|
+
"passport-google-oauth20": "^2.0.0",
|
|
111
|
+
"react": "18.3.1",
|
|
112
|
+
"react-i18next": "^16.3.4",
|
|
113
|
+
"react-query": "^3.39.3",
|
|
114
|
+
"@langchain/anthropic": "^0.3.33",
|
|
115
|
+
"@langchain/deepseek": "^0.1.0",
|
|
116
|
+
"@langchain/google-genai": "^0.2.18",
|
|
117
|
+
"@langchain/openai": "^0.6.16"
|
|
118
|
+
},
|
|
119
|
+
"devDependencies": {
|
|
120
|
+
"@eslint/js": "^9.32.0",
|
|
121
|
+
"concurrently": "^9.2.0",
|
|
122
|
+
"cross-env": "^10.0.0",
|
|
123
|
+
"eslint": "^9.32.0",
|
|
124
|
+
"globals": "^16.3.0"
|
|
125
|
+
},
|
|
126
|
+
"subPackages": [
|
|
127
|
+
"client"
|
|
128
|
+
],
|
|
129
|
+
"keywords": [
|
|
130
|
+
"data-driven engine",
|
|
131
|
+
"headless CMS",
|
|
132
|
+
"backend",
|
|
133
|
+
"automation",
|
|
134
|
+
"AWS S3",
|
|
135
|
+
"MongoDB"
|
|
136
|
+
],
|
|
137
|
+
"author": "anonympins",
|
|
138
|
+
"license": "MIT",
|
|
139
|
+
"engines": {
|
|
140
|
+
"node": ">=20.0.0"
|
|
141
|
+
}
|
|
142
|
+
}
|
package/src/defaultModels.js
CHANGED
|
@@ -84,6 +84,13 @@ export const defaultModels = {
|
|
|
84
84
|
"required": false,
|
|
85
85
|
"index": true,
|
|
86
86
|
"hint": "Si défini, l'exception (l'octroi ou la révocation) est temporaire."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "env",
|
|
90
|
+
"type": "relation",
|
|
91
|
+
"relation": "env",
|
|
92
|
+
"required": false,
|
|
93
|
+
"hint": "Si défini, la permission ne s'applique qu'à cet environnement. Sinon, elle est globale."
|
|
87
94
|
}
|
|
88
95
|
]
|
|
89
96
|
},
|
|
@@ -911,7 +918,8 @@ export const defaultModels = {
|
|
|
911
918
|
"tags": ["system", "automation"],
|
|
912
919
|
fields: [
|
|
913
920
|
{ name: 'name', type: 'string_t', required: true, hint: "Unique name for the workflow (e.g., 'Order Validation', 'Low Stock Notification')." },
|
|
914
|
-
{ name: 'description', type: 'richtext', hint: "Detailed explanation of the workflow's purpose."
|
|
921
|
+
{ name: 'description', type: 'richtext', hint: "Detailed explanation of the workflow's purpose."
|
|
922
|
+
},
|
|
915
923
|
{ name: 'startStep', type: 'relation', relation: 'workflowStep', required: false, hint: "The first step to execute when the workflow starts." }
|
|
916
924
|
]
|
|
917
925
|
},
|
|
@@ -975,7 +983,8 @@ export const defaultModels = {
|
|
|
975
983
|
"tags": ["system", "automation"],
|
|
976
984
|
fields: [
|
|
977
985
|
{ name: 'workflow', type: 'relation', relation: 'workflow', required: true, hint: "The workflow this step belongs to." },
|
|
978
|
-
{ name: 'name', type: 'string_t', hint: "Optional descriptive name for the step (e.g., 'Check Inventory', 'Send Confirmation Email')."
|
|
986
|
+
{ name: 'name', type: 'string_t', hint: "Optional descriptive name for the step (e.g., 'Check Inventory', 'Send Confirmation Email')."
|
|
987
|
+
},
|
|
979
988
|
{ name: 'conditions', type: 'code', language: 'json', conditionBuilder: true, hint: "Optional conditions checked before executing the step's action." },
|
|
980
989
|
{ name: 'actions', type: 'relation', relation: 'workflowAction', multiple: true, required: true, hint: "The main actions performed by this step." },
|
|
981
990
|
{ name: 'onSuccessStep', type: 'relation', relation: 'workflowStep', hint: "Optional: The next step if this step's action succeeds." },
|
|
@@ -990,6 +999,7 @@ export const defaultModels = {
|
|
|
990
999
|
"tags": ["system", "automation"],
|
|
991
1000
|
fields: [
|
|
992
1001
|
{ name: 'name', type: 'string_t', required: true, hint: "Name of the action (e.g., 'Update Order Status', 'Send Email', 'Call Payment API')." },
|
|
1002
|
+
{ name: 'workflow', type: 'relation', relation: 'workflow', required: false },
|
|
993
1003
|
{
|
|
994
1004
|
name: 'type',
|
|
995
1005
|
type: 'enum',
|