data-primals-engine 1.2.6 → 1.3.1

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +440 -0
  2. package/README.md +782 -712
  3. package/client/src/App.jsx +13 -20
  4. package/client/src/App.scss +4 -0
  5. package/client/src/AssistantChat.jsx +5 -4
  6. package/client/src/DataEditor.jsx +1 -1
  7. package/client/src/DataLayout.jsx +2 -2
  8. package/client/src/DataTable.jsx +66 -5
  9. package/client/src/ExportDialog.jsx +2 -2
  10. package/client/src/Field.jsx +5 -4
  11. package/client/src/HistoryDialog.jsx +96 -0
  12. package/client/src/HistoryDialog.scss +73 -0
  13. package/client/src/KanbanCard.jsx +4 -2
  14. package/client/src/KanbanConfigModal.jsx +5 -7
  15. package/client/src/ModelCreator.jsx +36 -15
  16. package/client/src/ModelCreatorField.jsx +14 -15
  17. package/client/src/PackGallery.jsx +89 -9
  18. package/client/src/PackGallery.scss +58 -4
  19. package/client/src/RTETrans.jsx +11 -0
  20. package/client/src/RelationValue.jsx +3 -4
  21. package/client/src/constants.js +1 -1
  22. package/client/src/core/data.js +2 -1
  23. package/client/src/hooks/useTutorials.jsx +1 -1
  24. package/client/src/translations.js +217 -0
  25. package/package.json +9 -2
  26. package/server.js +4 -4
  27. package/src/HistoryDialog.jsx +86 -0
  28. package/src/HistoryDialog.scss +77 -0
  29. package/src/constants.js +6 -0
  30. package/src/defaultModels.js +23 -10
  31. package/src/email.js +1 -1
  32. package/src/engine.js +6 -6
  33. package/src/events.js +15 -5
  34. package/src/gameObject.js +0 -29
  35. package/src/i18n.js +1 -1
  36. package/src/modules/{assistant.js → assistant/assistant.js} +42 -27
  37. package/src/modules/assistant/constants.js +16 -0
  38. package/src/modules/data/data.history.js +304 -0
  39. package/src/modules/data/data.js +306 -238
  40. package/src/modules/data/data.routes.js +33 -7
  41. package/src/modules/mongodb.js +17 -1
  42. package/src/modules/user.js +21 -4
  43. package/src/modules/workflow.js +133 -48
  44. package/src/packs.js +1016 -9
  45. package/src/services/index.js +11 -0
  46. package/src/services/stripe.js +195 -0
  47. package/src/workers/crypto-worker.js +3 -3
  48. package/test/data.backup.integration.test.js +5 -0
  49. package/test/data.history.integration.test.js +193 -0
  50. package/test/data.integration.test.js +144 -41
  51. package/test/events.test.js +27 -27
  52. package/test/globalTeardown.js +1 -0
  53. package/test/model.integration.test.js +5 -0
  54. package/test/workflow.actions.integration.test.js +4 -2
  55. package/test/workflow.integration.test.js +3 -1
  56. package/test/workflow.robustness.test.js +2 -0
@@ -17,29 +17,29 @@ describe('Event System', () => {
17
17
  }
18
18
  });
19
19
 
20
- it('should allow listening for and triggering an event', () => {
20
+ it('should allow listening for and triggering an event', async() => {
21
21
  const mockCallback = vitest.fn();
22
22
  const eventName = 'testEvent';
23
23
 
24
24
  Event.Listen(eventName, mockCallback);
25
- Event.Trigger(eventName);
25
+ await Event.Trigger(eventName);
26
26
 
27
27
  expect(mockCallback).toHaveBeenCalledTimes(1);
28
28
  });
29
- it('should pass arguments to the event callback', () => {
29
+ it('should pass arguments to the event callback', async() => {
30
30
  const mockCallback = vitest.fn();
31
31
  const eventName = 'testEventWithArgs';
32
32
  const arg1 = 'hello';
33
33
  const arg2 = 123;
34
34
 
35
35
  Event.Listen(eventName, mockCallback, "priority", "medium");
36
- Event.Trigger(eventName, "priority", "medium", arg1, arg2);
36
+ await Event.Trigger(eventName, "priority", "medium", arg1, arg2);
37
37
 
38
38
  expect(mockCallback).toHaveBeenCalledTimes(1);
39
39
  expect(mockCallback).toHaveBeenCalledWith(arg1, arg2);
40
40
  });
41
41
 
42
- it('should allow removing a specific callback', () => {
42
+ it('should allow removing a specific callback', async() => {
43
43
  const mockCallback1 = vitest.fn();
44
44
  const mockCallback2 = vitest.fn();
45
45
  const eventName = 'testEventRemoveCallback';
@@ -47,22 +47,22 @@ describe('Event System', () => {
47
47
  Event.Listen(eventName, mockCallback1);
48
48
  Event.Listen(eventName, mockCallback2);
49
49
  Event.RemoveCallback(eventName, mockCallback1);
50
- Event.Trigger(eventName);
50
+ await Event.Trigger(eventName);
51
51
 
52
52
  expect(mockCallback1).not.toHaveBeenCalled();
53
53
  expect(mockCallback2).toHaveBeenCalledTimes(1);
54
54
  });
55
55
 
56
- it('should not trigger a callback if the event is not listened to', () => {
56
+ it('should not trigger a callback if the event is not listened to', async() => {
57
57
  const mockCallback = vitest.fn();
58
58
  const eventName = 'nonExistentEvent';
59
59
 
60
- Event.Trigger(eventName);
60
+ await Event.Trigger(eventName);
61
61
 
62
62
  expect(mockCallback).not.toHaveBeenCalled();
63
63
  });
64
64
 
65
- it('should handle different systems and layers', () => {
65
+ it('should handle different systems and layers', async() => {
66
66
  const mockCallbackPriority = vitest.fn();
67
67
  const mockCallbackLog = vitest.fn();
68
68
  const eventName = 'testEventMultiSystem';
@@ -70,13 +70,13 @@ describe('Event System', () => {
70
70
  Event.Listen(eventName, mockCallbackPriority, 'priority', 'high');
71
71
  Event.Listen(eventName, mockCallbackLog, 'log', 'info');
72
72
 
73
- Event.Trigger(eventName, 'priority', 'high');
73
+ await Event.Trigger(eventName, 'priority', 'high');
74
74
  expect(mockCallbackPriority).toHaveBeenCalledTimes(1);
75
75
  expect(mockCallbackLog).not.toHaveBeenCalled();
76
76
 
77
77
  vitest.clearAllMocks(); // Reset mock counts
78
78
 
79
- Event.Trigger(eventName, 'log', 'info');
79
+ await Event.Trigger(eventName, 'log', 'info');
80
80
  expect(mockCallbackPriority).not.toHaveBeenCalled();
81
81
  expect(mockCallbackLog).toHaveBeenCalledTimes(1);
82
82
  });
@@ -92,7 +92,7 @@ describe('Event System', () => {
92
92
  });
93
93
 
94
94
  describe('Event.Trigger result merging', () => {
95
- it('should merge objects from multiple callbacks using spread operator', () => {
95
+ it('should merge objects from multiple callbacks using spread operator', async() => {
96
96
  const eventName = 'mergeObjectEvent';
97
97
  const callback1 = vitest.fn().mockReturnValue({ a: 1, b: 2 });
98
98
  const callback2 = vitest.fn().mockReturnValue({ b: 3, c: 4 }); // b will be overwritten
@@ -100,12 +100,12 @@ describe('Event System', () => {
100
100
  Event.Listen(eventName, callback1);
101
101
  Event.Listen(eventName, callback2);
102
102
 
103
- const result = Event.Trigger(eventName);
103
+ const result = await Event.Trigger(eventName);
104
104
 
105
105
  expect(result).toEqual({ a: 1, b: 3, c: 4 });
106
106
  });
107
107
 
108
- it('should concatenate arrays from multiple callbacks', () => {
108
+ it('should concatenate arrays from multiple callbacks', async() => {
109
109
  const eventName = 'mergeArrayEvent';
110
110
  const callback1 = vitest.fn().mockReturnValue([1, 2]);
111
111
  const callback2 = vitest.fn().mockReturnValue([3, 4]);
@@ -113,12 +113,12 @@ describe('Event System', () => {
113
113
  Event.Listen(eventName, callback1);
114
114
  Event.Listen(eventName, callback2);
115
115
 
116
- const result = Event.Trigger(eventName);
116
+ const result = await Event.Trigger(eventName);
117
117
 
118
118
  expect(result).toEqual([1, 2, 3, 4]);
119
119
  });
120
120
 
121
- it('should concatenate strings from multiple callbacks', () => {
121
+ it('should concatenate strings from multiple callbacks', async() => {
122
122
  const eventName = 'mergeStringEvent';
123
123
  const callback1 = vitest.fn().mockReturnValue('Hello ');
124
124
  const callback2 = vitest.fn().mockReturnValue('World');
@@ -126,12 +126,12 @@ describe('Event System', () => {
126
126
  Event.Listen(eventName, callback1);
127
127
  Event.Listen(eventName, callback2);
128
128
 
129
- const result = Event.Trigger(eventName);
129
+ const result = await Event.Trigger(eventName);
130
130
 
131
131
  expect(result).toBe('Hello World');
132
132
  });
133
133
 
134
- it('should add numbers from multiple callbacks', () => {
134
+ it('should add numbers from multiple callbacks', async() => {
135
135
  const eventName = 'mergeNumberEvent';
136
136
  const callback1 = vitest.fn().mockReturnValue(10);
137
137
  const callback2 = vitest.fn().mockReturnValue(20);
@@ -139,12 +139,12 @@ describe('Event System', () => {
139
139
  Event.Listen(eventName, callback1);
140
140
  Event.Listen(eventName, callback2);
141
141
 
142
- const result = Event.Trigger(eventName);
142
+ const result = await Event.Trigger(eventName);
143
143
 
144
144
  expect(result).toBe(30);
145
145
  });
146
146
 
147
- it('should perform a logical AND on booleans from multiple callbacks', () => {
147
+ it('should perform a logical AND on booleans from multiple callbacks', async() => {
148
148
  const eventName = 'mergeBooleanEvent';
149
149
 
150
150
  // Case 1: true && true -> true
@@ -152,23 +152,23 @@ describe('Event System', () => {
152
152
  const cb_true2 = vitest.fn().mockReturnValue(true);
153
153
  Event.Listen(eventName, cb_true1);
154
154
  Event.Listen(eventName, cb_true2);
155
- expect(Event.Trigger(eventName)).toBe(true);
155
+ expect(await Event.Trigger(eventName)).toBe(true);
156
156
  Event.RemoveAllCallbacks(eventName); // Clean up for next case
157
157
 
158
158
  // Case 2: true && false -> false
159
159
  const cb_false1 = vitest.fn().mockReturnValue(false);
160
160
  Event.Listen(eventName, cb_true1);
161
161
  Event.Listen(eventName, cb_false1);
162
- expect(Event.Trigger(eventName)).toBe(false);
162
+ expect(await Event.Trigger(eventName)).toBe(false);
163
163
  Event.RemoveAllCallbacks(eventName);
164
164
 
165
165
  // Case 3: false && true -> false
166
166
  Event.Listen(eventName, cb_false1);
167
167
  Event.Listen(eventName, cb_true1);
168
- expect(Event.Trigger(eventName)).toBe(false);
168
+ expect(await Event.Trigger(eventName)).toBe(false);
169
169
  });
170
170
 
171
- it('should handle mixed-type return values', () => {
171
+ it('should handle mixed-type return values', async() => {
172
172
  const eventName = 'mergeMixedEvent';
173
173
  const callback1 = vitest.fn().mockReturnValue(100); // number
174
174
  const callback2 = vitest.fn().mockReturnValue({ a: 1 }); // object
@@ -176,7 +176,7 @@ describe('Event System', () => {
176
176
  Event.Listen(eventName, callback1);
177
177
  Event.Listen(eventName, callback2);
178
178
 
179
- const result = Event.Trigger(eventName);
179
+ const result = await Event.Trigger(eventName);
180
180
 
181
181
  // The logic initializes `ret` with the first value (100).
182
182
  // When the second callback returns an object, it re-initializes `ret` to {}
@@ -184,7 +184,7 @@ describe('Event System', () => {
184
184
  expect(result).toEqual({ a: 1 });
185
185
  });
186
186
 
187
- it('should ignore null and undefined return values while merging', () => {
187
+ it('should ignore null and undefined return values while merging', async() => {
188
188
  const eventName = 'ignoreNullsEvent';
189
189
  const callback1 = vitest.fn().mockReturnValue(null);
190
190
  const callback2 = vitest.fn().mockReturnValue({ data: 'important' });
@@ -194,7 +194,7 @@ describe('Event System', () => {
194
194
  Event.Listen(eventName, callback2);
195
195
  Event.Listen(eventName, callback3);
196
196
 
197
- const result = Event.Trigger(eventName);
197
+ const result = await Event.Trigger(eventName);
198
198
 
199
199
  expect(result).toEqual({ data: 'important' });
200
200
  });
@@ -3,6 +3,7 @@ import { stopEngine } from '../src/setenv.js';
3
3
 
4
4
  export default async function () {
5
5
  console.log('\n--- Global Teardown: Stopping engine and database ---');
6
+
6
7
  await stopEngine();
7
8
  console.log('--- Global Teardown: Cleanup complete. ---');
8
9
  }
@@ -9,6 +9,7 @@ import {
9
9
  import {generateUniqueName, initEngine} from "../src/setenv.js";
10
10
  import {editModel} from "../src/index.js";
11
11
  import {getCollectionForUser} from "../src/modules/mongodb.js";
12
+ import {purgeData} from "../src/modules/data/data.history.js";
12
13
 
13
14
  let testModelsColInstance;
14
15
  let testModelId;
@@ -101,6 +102,10 @@ async function setupTestContext() {
101
102
  ]);
102
103
 
103
104
  testModelId = result.insertedIds[0];
105
+
106
+ await purgeData(currentTestUser, comprehensiveTestModelDefinition.name);
107
+ await purgeData(currentTestUser, relatedModelDefinition.name);
108
+
104
109
  await testDatasColInstance.deleteMany({ _user: currentTestUser.username });
105
110
  await testDatasColInstance.deleteMany({ _model: { $in: [comprehensiveTestModelDefinition.name, 'renamedTestModel'] } });
106
111
  // Retourner toutes les variables nécessaires pour un test
@@ -11,6 +11,7 @@ import { initEngine } from "../src/setenv.js";
11
11
  import * as emailModule from '../src/email.js';
12
12
  import { ChatOpenAI } from "@langchain/openai";
13
13
  import {ObjectId} from "mongodb";
14
+ import {purgeData} from "../src/modules/data/data.history.js";
14
15
 
15
16
  let testModelsColInstance;
16
17
  let testDatasColInstance;
@@ -142,6 +143,7 @@ afterEach(() => {
142
143
  });
143
144
 
144
145
  afterAll(async () => {
146
+ await purgeData(mockUser);
145
147
  const coll = await getCollectionForUser(mockUser);
146
148
  await coll.drop();
147
149
  });
@@ -251,7 +253,7 @@ describe('Intégration des Actions de Workflow', () => {
251
253
  expect(deletedTask).toBeNull();
252
254
  });
253
255
 
254
- it('Action Webhook: devrait appeler fetch avec les bonnes informations substituées', async () => {
256
+ it('Action HttpRequest: devrait appeler fetch avec les bonnes informations substituées', async () => {
255
257
  global.fetch.mockResolvedValue({
256
258
  ok: true,
257
259
  status: 200,
@@ -261,7 +263,7 @@ describe('Intégration des Actions de Workflow', () => {
261
263
 
262
264
  const { workflowId } = await setupWorkflow({
263
265
  name: 'Notify External System',
264
- type: 'Webhook',
266
+ type: 'HttpRequest',
265
267
  method: 'POST',
266
268
  url: 'https://api.example.com/notify/{triggerData.status}',
267
269
  headers: { "Authorization": "Bearer {env.API_KEY}" },
@@ -5,6 +5,7 @@ import {insertData, editData, deleteData, patchData} from '../src/index.js';
5
5
  import { modelsCollection as getAppModelsCollection, getCollectionForUser } from '../src/modules/mongodb.js';
6
6
  import * as workflowModule from '../src/modules/workflow.js';
7
7
  import {initEngine} from "../src/setenv.js";
8
+ import {purgeData} from "../src/modules/data/data.history.js";
8
9
 
9
10
  beforeAll(async () =>{
10
11
  Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
@@ -113,7 +114,7 @@ const workflowMetaModels = [
113
114
  { name: 'updateMultiple', type: 'boolean' },
114
115
  { name: 'deleteMultiple', type: 'boolean' },
115
116
  // GenerateAIContent
116
- { name: 'aiProvider', type: 'enum', items: ['OpenAI', 'GoogleGemini'] },
117
+ { name: 'aiProvider', type: 'enum', items: ['OpenAI', 'Google', 'Anthropic', 'DeepSeek'] },
117
118
  { name: 'aiModel', type: 'string' },
118
119
  { name: 'prompt', type: 'richtext' },
119
120
  // SendEmail
@@ -157,6 +158,7 @@ afterEach(() => {
157
158
  vi.useRealTimers()
158
159
  })
159
160
  afterAll(async () => {
161
+ await purgeData(mockUser);
160
162
  const coll = await getCollectionForUser(mockUser);
161
163
  await coll.drop();
162
164
  })
@@ -7,6 +7,7 @@ import { modelsCollection as getAppModelsCollection, getCollectionForUser, getCo
7
7
  import * as workflowModule from '../src/modules/workflow.js';
8
8
  import {initEngine} from "../src/setenv.js";
9
9
  import {maxExecutionsByStep} from "../src/constants.js";
10
+ import {purgeData} from "../src/modules/data/data.history.js";
10
11
 
11
12
  vi.mock('../src/modules/workflow.js', { spy: true })
12
13
 
@@ -54,6 +55,7 @@ afterEach(() => {
54
55
  vi.useRealTimers()
55
56
  })
56
57
  afterAll(async () => {
58
+ await purgeData(mockUser);
57
59
  const coll = await getCollectionForUser(mockUser);
58
60
  await coll.drop();
59
61
  })