data-primals-engine 1.5.2 → 1.6.0

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 (63) hide show
  1. package/README.md +924 -915
  2. package/client/README.md +136 -136
  3. package/client/index.js +0 -1
  4. package/client/public/doc/API.postman_collection.json +213 -213
  5. package/client/public/react.svg +9 -9
  6. package/client/src/AddWidgetTypeModal.jsx +47 -47
  7. package/client/src/App.css +42 -42
  8. package/client/src/App.jsx +92 -150
  9. package/client/src/App.scss +6 -0
  10. package/client/src/AssistantChat.jsx +362 -363
  11. package/client/src/Button.jsx +12 -12
  12. package/client/src/Dashboard.jsx +480 -480
  13. package/client/src/DashboardHtmlViewItem.jsx +146 -146
  14. package/client/src/DashboardView.jsx +654 -654
  15. package/client/src/DataEditor.jsx +383 -383
  16. package/client/src/DataLayout.jsx +779 -808
  17. package/client/src/DataTable.jsx +782 -822
  18. package/client/src/DataTable.scss +186 -186
  19. package/client/src/GeolocationField.jsx +93 -93
  20. package/client/src/HistoryDialog.jsx +307 -307
  21. package/client/src/HistoryDialog.scss +319 -319
  22. package/client/src/HtmlViewBuilderModal.jsx +90 -90
  23. package/client/src/HtmlViewBuilderModal.scss +17 -17
  24. package/client/src/HtmlViewCard.jsx +43 -43
  25. package/client/src/ModelCreatorField.jsx +950 -950
  26. package/client/src/ModelList.jsx +280 -280
  27. package/client/src/Notification.jsx +136 -136
  28. package/client/src/PackGallery.jsx +391 -391
  29. package/client/src/PackGallery.scss +231 -231
  30. package/client/src/Pagination.jsx +143 -143
  31. package/client/src/RelationField.jsx +354 -354
  32. package/client/src/RelationSelectorWidget.jsx +172 -172
  33. package/client/src/RelationValue.jsx +2 -1
  34. package/client/src/contexts/CommandContext.jsx +260 -0
  35. package/client/src/contexts/ModelContext.jsx +4 -1
  36. package/client/src/contexts/UIContext.jsx +72 -72
  37. package/client/src/filter.js +263 -263
  38. package/client/src/index.css +90 -90
  39. package/package.json +6 -5
  40. package/perf/artillery-hooks.js +37 -37
  41. package/perf/setup.yml +25 -25
  42. package/src/constants.js +4 -0
  43. package/src/engine.js +335 -335
  44. package/src/events.js +232 -137
  45. package/src/filter.js +274 -274
  46. package/src/modules/assistant/assistant.js +225 -83
  47. package/src/modules/auth-google/index.js +50 -50
  48. package/src/modules/auth-microsoft/index.js +81 -81
  49. package/src/modules/data/data.core.js +118 -118
  50. package/src/modules/data/data.history.js +555 -555
  51. package/src/modules/data/data.operations.js +3401 -3381
  52. package/src/modules/data/data.relations.js +686 -686
  53. package/src/modules/data/data.routes.js +1879 -1879
  54. package/src/modules/data/data.validation.js +2 -2
  55. package/src/modules/file.js +247 -247
  56. package/src/packs.js +2 -2
  57. package/src/providers.js +2 -2
  58. package/src/services/stripe.js +196 -196
  59. package/src/sso.js +193 -193
  60. package/test/data.history.integration.test.js +264 -264
  61. package/test/data.integration.test.js +1206 -1206
  62. package/test/events.test.js +108 -10
  63. package/test/model.integration.test.js +377 -377
@@ -1,82 +1,82 @@
1
- import { Sso, SSOUserProvider } from '../../sso.js';
2
- import { Logger } from "../../gameObject.js";
3
-
4
- let logger;
5
-
6
- export async function onInit(engine) {
7
- logger = engine.getComponent(Logger);
8
-
9
- try {
10
- // 1. Tenter d'importer dynamiquement la stratégie Azure AD.
11
- // L'utilisateur devra exécuter `npm install passport-azure-ad`.
12
- const { OIDCStrategy } = await import('passport-azure-ad');
13
-
14
- const ssoUserProvider = new SSOUserProvider(engine);
15
-
16
- // 2. Récupérer ou créer le composant SSO central.
17
- let ssoComponent = engine.getComponent(Sso);
18
- if (!ssoComponent) {
19
- ssoComponent = engine.addComponent(Sso);
20
- ssoComponent.initialize({ ssoUserProvider });
21
- ssoComponent.addLogoutRoute();
22
- logger.info("[auth-microsoft] Sso component was not found, created and initialized a new one.");
23
- }
24
-
25
- // 3. Vérifier les variables d'environnement critiques pour Azure AD.
26
- const { MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, MICROSOFT_TENANT_ID } = process.env;
27
- if (!MICROSOFT_CLIENT_ID || !MICROSOFT_CLIENT_SECRET || !MICROSOFT_TENANT_ID) {
28
- logger.warn("[auth-microsoft] Microsoft environment variables are not fully set. Microsoft SSO will be disabled.");
29
- return;
30
- }
31
-
32
- const callbackUrl = process.env.MICROSOFT_CALLBACK_URL || '/api/auth/microsoft/callback';
33
-
34
- // 4. Créer l'instance de la stratégie OIDC pour Microsoft.
35
- const microsoftStrategy = new OIDCStrategy({
36
- identityMetadata: `https://login.microsoftonline.com/${MICROSOFT_TENANT_ID}/v2.0/.well-known/openid-configuration`,
37
- clientID: MICROSOFT_CLIENT_ID,
38
- responseType: 'code id_token',
39
- responseMode: 'form_post',
40
- redirectUrl: callbackUrl,
41
- allowHttpForRedirectUrl: process.env.NODE_ENV !== 'production',
42
- clientSecret: MICROSOFT_CLIENT_SECRET,
43
- validateIssuer: false, // Important pour les tenants multiples
44
- passReqToCallback: false,
45
- scope: ['profile', 'email', 'openid']
46
- },
47
- async (iss, sub, profile, accessToken, refreshToken, done) => {
48
- try {
49
- // Le profil Azure AD est différent. On l'adapte à notre format standard.
50
- const email = profile.upn || profile._json.email;
51
- if (!email) {
52
- return done(new Error("Microsoft profile is missing an email (upn)."));
53
- }
54
-
55
- const adaptedProfile = {
56
- provider: 'azuread',
57
- id: profile.oid, // Object ID de l'utilisateur dans Azure
58
- displayName: profile.displayName,
59
- emails: [{ value: email }]
60
- };
61
-
62
- const user = await ssoUserProvider.findOrCreate(adaptedProfile);
63
- return done(null, user);
64
- } catch (err) {
65
- return done(err);
66
- }
67
- });
68
-
69
- // 5. Enregistrer la stratégie auprès du composant SSO.
70
- ssoComponent.addStrategy('azuread-openidconnect', microsoftStrategy, {
71
- authPath: '/api/auth/microsoft',
72
- callbackPath: callbackUrl
73
- });
74
-
75
- } catch (e) {
76
- if (e.code === 'ERR_MODULE_NOT_FOUND') {
77
- logger.info("[auth-microsoft] Module désactivé. Pour l'activer, exécutez 'npm install passport-azure-ad'");
78
- } else {
79
- logger.error("[auth-microsoft] Une erreur inattendue a empêché le chargement du module :", e);
80
- }
81
- }
1
+ import { Sso, SSOUserProvider } from '../../sso.js';
2
+ import { Logger } from "../../gameObject.js";
3
+
4
+ let logger;
5
+
6
+ export async function onInit(engine) {
7
+ logger = engine.getComponent(Logger);
8
+
9
+ try {
10
+ // 1. Tenter d'importer dynamiquement la stratégie Azure AD.
11
+ // L'utilisateur devra exécuter `npm install passport-azure-ad`.
12
+ const { OIDCStrategy } = await import('passport-azure-ad');
13
+
14
+ const ssoUserProvider = new SSOUserProvider(engine);
15
+
16
+ // 2. Récupérer ou créer le composant SSO central.
17
+ let ssoComponent = engine.getComponent(Sso);
18
+ if (!ssoComponent) {
19
+ ssoComponent = engine.addComponent(Sso);
20
+ ssoComponent.initialize({ ssoUserProvider });
21
+ ssoComponent.addLogoutRoute();
22
+ logger.info("[auth-microsoft] Sso component was not found, created and initialized a new one.");
23
+ }
24
+
25
+ // 3. Vérifier les variables d'environnement critiques pour Azure AD.
26
+ const { MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, MICROSOFT_TENANT_ID } = process.env;
27
+ if (!MICROSOFT_CLIENT_ID || !MICROSOFT_CLIENT_SECRET || !MICROSOFT_TENANT_ID) {
28
+ logger.warn("[auth-microsoft] Microsoft environment variables are not fully set. Microsoft SSO will be disabled.");
29
+ return;
30
+ }
31
+
32
+ const callbackUrl = process.env.MICROSOFT_CALLBACK_URL || '/api/auth/microsoft/callback';
33
+
34
+ // 4. Créer l'instance de la stratégie OIDC pour Microsoft.
35
+ const microsoftStrategy = new OIDCStrategy({
36
+ identityMetadata: `https://login.microsoftonline.com/${MICROSOFT_TENANT_ID}/v2.0/.well-known/openid-configuration`,
37
+ clientID: MICROSOFT_CLIENT_ID,
38
+ responseType: 'code id_token',
39
+ responseMode: 'form_post',
40
+ redirectUrl: callbackUrl,
41
+ allowHttpForRedirectUrl: process.env.NODE_ENV !== 'production',
42
+ clientSecret: MICROSOFT_CLIENT_SECRET,
43
+ validateIssuer: false, // Important pour les tenants multiples
44
+ passReqToCallback: false,
45
+ scope: ['profile', 'email', 'openid']
46
+ },
47
+ async (iss, sub, profile, accessToken, refreshToken, done) => {
48
+ try {
49
+ // Le profil Azure AD est différent. On l'adapte à notre format standard.
50
+ const email = profile.upn || profile._json.email;
51
+ if (!email) {
52
+ return done(new Error("Microsoft profile is missing an email (upn)."));
53
+ }
54
+
55
+ const adaptedProfile = {
56
+ provider: 'azuread',
57
+ id: profile.oid, // Object ID de l'utilisateur dans Azure
58
+ displayName: profile.displayName,
59
+ emails: [{ value: email }]
60
+ };
61
+
62
+ const user = await ssoUserProvider.findOrCreate(adaptedProfile);
63
+ return done(null, user);
64
+ } catch (err) {
65
+ return done(err);
66
+ }
67
+ });
68
+
69
+ // 5. Enregistrer la stratégie auprès du composant SSO.
70
+ ssoComponent.addStrategy('azuread-openidconnect', microsoftStrategy, {
71
+ authPath: '/api/auth/microsoft',
72
+ callbackPath: callbackUrl
73
+ });
74
+
75
+ } catch (e) {
76
+ if (e.code === 'ERR_MODULE_NOT_FOUND') {
77
+ logger.info("[auth-microsoft] Module désactivé. Pour l'activer, exécutez 'npm install passport-azure-ad'");
78
+ } else {
79
+ logger.error("[auth-microsoft] Une erreur inattendue a empêché le chargement du module :", e);
80
+ }
81
+ }
82
82
  }
@@ -1,119 +1,119 @@
1
- import NodeCache from "node-cache";
2
- import path from "node:path";
3
- import {Worker} from 'worker_threads';
4
- import {fileURLToPath} from "node:url";
5
- export const modelsCache = new NodeCache( { stdTTL: 100, checkperiod: 120 } );
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = path.dirname(__filename);
9
-
10
- /**
11
- * Helper to escape characters for regex.
12
- * @param {string} str The string to escape.
13
- * @returns {string} The escaped string.
14
- */
15
- function escapeRegex(str) {
16
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
- }
18
-
19
- /**
20
- * Generates a regular expression from a mask pattern and replacement rules.
21
- * @param {string} mask - The mask pattern, e.g., '+0 (___) ___-__-__'.
22
- * @param {object} replacement - An object mapping placeholder characters to regex patterns, e.g., { "_": "\\d" }.
23
- * @returns {string|null} - A regular expression string or null if inputs are invalid.
24
- */
25
- export function generateRegexFromMask(mask, replacement) {
26
- if (!mask || !replacement || typeof replacement !== 'object') return null;
27
-
28
- let regex = '^';
29
- for (const char of mask) {
30
- regex += replacement[char] ? `(${replacement[char]})` : escapeRegex(char);
31
- }
32
- regex += '$';
33
- return regex;
34
- }
35
-
36
- export const mongoDBWhitelist = [
37
- "$$NOW", "$in", "$eq", "$gt", "$gte", "$in", "$lt", "$lte", "$ne", "$nin", "$type", "$size",
38
- "$and", "$not", "$nor", "$or", "$regexMatch", "$find", "$elemMatch", "$filter", "$toString", "$toObjectId",
39
- "$concat",
40
- '$add', '$subtract', '$multiply', '$divide', '$mod', '$pow', "$sqrt",
41
- "$rand",
42
- "$abs", '$sin', '$cos', '$tan', '$asin', '$acos', '$atan',
43
- "$toDate", "$toBool", "$toString", "$toInt", "$toDouble",
44
- "$dateDiff", "$dateSubtract", "$dateAdd", "$dateToString",
45
- '$year', '$month', '$week', '$dayOfMonth', '$dayOfWeek', '$dayOfYear', '$hour', '$minute', '$second', '$millisecond',
46
- '$geoNear', '$regex', '$text', '$nearSphere','$geoWithin','$geoIntersects'
47
- ];
48
- export let importJobs = {};
49
-
50
-
51
- /**
52
- * Exécute une tâche d'import/export (parsing, stringify) dans un worker thread.
53
- * @param {('parse-json'|'parse-csv'|'stringify-json')} action - L'action à effectuer.
54
- * @param {object} payload - Les données nécessaires pour l'action.
55
- * @returns {Promise<any>} - Une promesse qui se résout avec les données traitées.
56
- */
57
- export function runImportExportWorker(action, payload) {
58
- return new Promise((resolve, reject) => {
59
- const workerPath = path.resolve(process.cwd(), './src/workers/import-export-worker.js');
60
- const worker = new Worker(workerPath);
61
-
62
- worker.postMessage({ action, payload });
63
-
64
- worker.on('message', (result) => {
65
- if (result.success) {
66
- resolve(result.data);
67
- } else {
68
- // Correction : On s'assure de toujours passer une chaîne de caractères à new Error()
69
- const errorMessage = result.error || `Import/Export Worker failed with an unknown error. Action: ${action}.`;
70
- reject(new Error(errorMessage));
71
- }
72
- worker.terminate();
73
- });
74
-
75
- worker.on('error', (err) => {
76
- reject(err);
77
- worker.terminate();
78
- });
79
-
80
- worker.on('exit', (code) => {
81
- if (code !== 0) {
82
- reject(new Error(`Import/Export Worker stopped with exit code ${code}`));
83
- }
84
- });
85
- });
86
- }
87
- /** Exécute une tâche de cryptographie dans un worker thread.
88
- * @param {('encrypt'|'decrypt'|'hash')} action - L'action à effectuer.
89
- * @param {object} payload - Les données nécessaires pour l'action.
90
- * @returns {Promise<any>} - Une promesse qui se résout avec le résultat (si pertinent).
91
- */
92
- export function runCryptoWorkerTask(action, payload) {
93
- return new Promise((resolve, reject) => {
94
- const workerPath = path.resolve(__dirname, '../../workers/crypto-worker.js');
95
- const worker = new Worker(workerPath);
96
-
97
- worker.postMessage({ action, payload });
98
-
99
- worker.on('message', (result) => {
100
- if (result.success) {
101
- resolve(result.data); // Résout avec les données (ex: le hash) ou undefined si pas de retour
102
- } else {
103
- reject(new Error(result.error));
104
- }
105
- worker.terminate();
106
- });
107
-
108
- worker.on('error', (err) => {
109
- reject(err);
110
- worker.terminate();
111
- });
112
-
113
- worker.on('exit', (code) => {
114
- if (code !== 0) {
115
- reject(new Error(`Crypto Worker stopped with exit code ${code}`));
116
- }
117
- });
118
- });
1
+ import NodeCache from "node-cache";
2
+ import path from "node:path";
3
+ import {Worker} from 'worker_threads';
4
+ import {fileURLToPath} from "node:url";
5
+ export const modelsCache = new NodeCache( { stdTTL: 100, checkperiod: 120 } );
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ /**
11
+ * Helper to escape characters for regex.
12
+ * @param {string} str The string to escape.
13
+ * @returns {string} The escaped string.
14
+ */
15
+ function escapeRegex(str) {
16
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
+ }
18
+
19
+ /**
20
+ * Generates a regular expression from a mask pattern and replacement rules.
21
+ * @param {string} mask - The mask pattern, e.g., '+0 (___) ___-__-__'.
22
+ * @param {object} replacement - An object mapping placeholder characters to regex patterns, e.g., { "_": "\\d" }.
23
+ * @returns {string|null} - A regular expression string or null if inputs are invalid.
24
+ */
25
+ export function generateRegexFromMask(mask, replacement) {
26
+ if (!mask || !replacement || typeof replacement !== 'object') return null;
27
+
28
+ let regex = '^';
29
+ for (const char of mask) {
30
+ regex += replacement[char] ? `(${replacement[char]})` : escapeRegex(char);
31
+ }
32
+ regex += '$';
33
+ return regex;
34
+ }
35
+
36
+ export const mongoDBWhitelist = [
37
+ "$$NOW", "$in", "$eq", "$gt", "$gte", "$in", "$lt", "$lte", "$ne", "$nin", "$type", "$size",
38
+ "$and", "$not", "$nor", "$or", "$regexMatch", "$find", "$elemMatch", "$filter", "$toString", "$toObjectId",
39
+ "$concat",
40
+ '$add', '$subtract', '$multiply', '$divide', '$mod', '$pow', "$sqrt",
41
+ "$rand",
42
+ "$abs", '$sin', '$cos', '$tan', '$asin', '$acos', '$atan',
43
+ "$toDate", "$toBool", "$toString", "$toInt", "$toDouble",
44
+ "$dateDiff", "$dateSubtract", "$dateAdd", "$dateToString",
45
+ '$year', '$month', '$week', '$dayOfMonth', '$dayOfWeek', '$dayOfYear', '$hour', '$minute', '$second', '$millisecond',
46
+ '$geoNear', '$regex', '$text', '$nearSphere','$geoWithin','$geoIntersects'
47
+ ];
48
+ export let importJobs = {};
49
+
50
+
51
+ /**
52
+ * Exécute une tâche d'import/export (parsing, stringify) dans un worker thread.
53
+ * @param {('parse-json'|'parse-csv'|'stringify-json')} action - L'action à effectuer.
54
+ * @param {object} payload - Les données nécessaires pour l'action.
55
+ * @returns {Promise<any>} - Une promesse qui se résout avec les données traitées.
56
+ */
57
+ export function runImportExportWorker(action, payload) {
58
+ return new Promise((resolve, reject) => {
59
+ const workerPath = path.resolve(process.cwd(), './src/workers/import-export-worker.js');
60
+ const worker = new Worker(workerPath);
61
+
62
+ worker.postMessage({ action, payload });
63
+
64
+ worker.on('message', (result) => {
65
+ if (result.success) {
66
+ resolve(result.data);
67
+ } else {
68
+ // Correction : On s'assure de toujours passer une chaîne de caractères à new Error()
69
+ const errorMessage = result.error || `Import/Export Worker failed with an unknown error. Action: ${action}.`;
70
+ reject(new Error(errorMessage));
71
+ }
72
+ worker.terminate();
73
+ });
74
+
75
+ worker.on('error', (err) => {
76
+ reject(err);
77
+ worker.terminate();
78
+ });
79
+
80
+ worker.on('exit', (code) => {
81
+ if (code !== 0) {
82
+ reject(new Error(`Import/Export Worker stopped with exit code ${code}`));
83
+ }
84
+ });
85
+ });
86
+ }
87
+ /** Exécute une tâche de cryptographie dans un worker thread.
88
+ * @param {('encrypt'|'decrypt'|'hash')} action - L'action à effectuer.
89
+ * @param {object} payload - Les données nécessaires pour l'action.
90
+ * @returns {Promise<any>} - Une promesse qui se résout avec le résultat (si pertinent).
91
+ */
92
+ export function runCryptoWorkerTask(action, payload) {
93
+ return new Promise((resolve, reject) => {
94
+ const workerPath = path.resolve(__dirname, '../../workers/crypto-worker.js');
95
+ const worker = new Worker(workerPath);
96
+
97
+ worker.postMessage({ action, payload });
98
+
99
+ worker.on('message', (result) => {
100
+ if (result.success) {
101
+ resolve(result.data); // Résout avec les données (ex: le hash) ou undefined si pas de retour
102
+ } else {
103
+ reject(new Error(result.error));
104
+ }
105
+ worker.terminate();
106
+ });
107
+
108
+ worker.on('error', (err) => {
109
+ reject(err);
110
+ worker.terminate();
111
+ });
112
+
113
+ worker.on('exit', (code) => {
114
+ if (code !== 0) {
115
+ reject(new Error(`Crypto Worker stopped with exit code ${code}`));
116
+ }
117
+ });
118
+ });
119
119
  }