data-primals-engine 1.5.1 → 1.5.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/src/engine.js CHANGED
@@ -20,7 +20,7 @@ import sirv from "sirv";
20
20
  import * as tls from "node:tls";
21
21
  import {Event} from "./events.js";
22
22
  import path from "node:path";
23
- import { fileURLToPath } from 'node:url';
23
+ import { fileURLToPath, pathToFileURL } from 'node:url';
24
24
  import {validateModelStructure} from "./modules/data/data.validation.js";
25
25
  import { setSafeRegex } from "./filter.js";
26
26
  import safeRegexCallback from "safe-regex";
@@ -106,6 +106,9 @@ export const Engine = {
106
106
  console.log("Creating engine", Config.Get('modules'));
107
107
  const logger = engine.addComponent(Logger);
108
108
 
109
+ // Expose the Event bus on the engine instance for dependency injection
110
+ engine.Event = Event;
111
+
109
112
  engine.userProvider = new DefaultUserProvider(engine);
110
113
 
111
114
  engine.setUserProvider = (providerInstance) => {
@@ -157,56 +160,93 @@ export const Engine = {
157
160
  return engine._modules.find(m => m.module === module);
158
161
  };
159
162
 
160
- const importModule = async (module) => {
161
- const moduleA = await import(module);
162
- if (moduleA.onInit){
163
- await moduleA.onInit(engine);
164
- return {...moduleA, module};
165
- }else {
166
- const mod = moduleA.default();
167
- await mod?.onInit(engine);
168
- return { ...mod, module};
163
+ const importAndPrepareModule = async (moduleEntryPoint, moduleName) => {
164
+ const moduleA = await import(moduleEntryPoint);
165
+
166
+ let moduleInstance = null;
167
+ let onInitFunction = null;
168
+
169
+ // Cas 1: `export default { onInit }`
170
+ if (moduleA.default && typeof moduleA.default.onInit === 'function') {
171
+ moduleInstance = moduleA.default;
172
+ onInitFunction = moduleA.default.onInit;
169
173
  }
174
+ // Cas 2: `export async function onInit() {}`
175
+ else if (typeof moduleA.onInit === 'function') {
176
+ moduleInstance = moduleA;
177
+ onInitFunction = moduleA.onInit;
178
+ }
179
+
180
+ if (moduleInstance) {
181
+ // On stocke la fonction onInit pour plus tard et on retourne l'instance
182
+ // avec le nom court du module.
183
+ return { ...moduleInstance, onInit: onInitFunction, module: moduleName };
184
+ }
185
+
186
+ logger.warn(`Module loaded from ${moduleEntryPoint} does not export an onInit function or a default object with onInit.`);
187
+ return null;
170
188
  };
171
189
 
172
190
  engine._modules = [];
173
- for (const moduleIdentifier of Config.Get('modules', [])) {
174
- try {
175
- let moduleDir;
176
- const moduleName = path.basename(moduleIdentifier);
177
-
178
- const directPath = path.resolve(moduleIdentifier);
179
- let isDir = fs.existsSync(directPath) && fs.statSync(directPath).isDirectory();
180
191
 
181
- if (isDir) {
182
- moduleDir = directPath;
192
+ // On charge uniquement les modules spécifiés dans la configuration.
193
+ const allModules = Config.Get('modules', []);
194
+ const loadedModules = []; // Liste temporaire pour la phase 1
195
+ for (const moduleIdentifier of allModules) {
196
+ let moduleEntryPoint = null;
197
+ const moduleName = path.basename(moduleIdentifier, '.js');
198
+ try {
199
+ // 1. Tenter de résoudre comme un chemin (relatif au projet ou absolu)
200
+ const externalPath = path.resolve(process.cwd(), moduleIdentifier);
201
+ if (fs.existsSync(externalPath)) {
202
+ const stats = fs.statSync(externalPath);
203
+ if (stats.isDirectory()) {
204
+ // C'est un répertoire, on cherche le point d'entrée
205
+ const indexJsPath = path.join(externalPath, 'index.js');
206
+ const moduleJsPath = path.join(externalPath, `${moduleName}.js`);
207
+ if (fs.existsSync(indexJsPath)) moduleEntryPoint = pathToFileURL(indexJsPath).href;
208
+ else if (fs.existsSync(moduleJsPath)) moduleEntryPoint = pathToFileURL(moduleJsPath).href;
209
+ } else {
210
+ // C'est un fichier
211
+ moduleEntryPoint = pathToFileURL(externalPath).href;
212
+ }
183
213
  } else {
184
- moduleDir = path.resolve(__dirname, 'modules', moduleIdentifier);
185
- }
186
-
187
- let moduleEntryPoint;
188
- const jsPath = moduleDir + '.js';
189
- const indexJsPath = path.join(moduleDir, 'index.js');
190
- const moduleJsPath = path.join(moduleDir, `${moduleName}.js`);
191
-
192
- if (fs.existsSync(jsPath)) {
193
- moduleEntryPoint = 'file://' + jsPath;
194
- } else if (fs.existsSync(indexJsPath)) {
195
- moduleEntryPoint = 'file://' + indexJsPath;
196
- } else if (fs.existsSync(moduleJsPath)) {
197
- moduleEntryPoint = 'file://' + moduleJsPath;
214
+ // 2. Si ce n'est pas un chemin, tenter de résoudre comme un module interne
215
+ const internalDir = path.resolve(__dirname, 'modules', moduleIdentifier);
216
+ const internalFile = path.resolve(__dirname, 'modules', `${moduleIdentifier}.js`);
217
+
218
+ if (fs.existsSync(internalDir) && fs.statSync(internalDir).isDirectory()) {
219
+ // C'est un répertoire de module interne
220
+ const indexJsPath = path.join(internalDir, 'index.js');
221
+ const moduleJsPath = path.join(internalDir, `${moduleIdentifier}.js`);
222
+ if (fs.existsSync(indexJsPath)) moduleEntryPoint = pathToFileURL(indexJsPath).href;
223
+ else if (fs.existsSync(moduleJsPath)) moduleEntryPoint = pathToFileURL(moduleJsPath).href;
224
+ } else if (fs.existsSync(internalFile)) {
225
+ // C'est un fichier de module interne
226
+ moduleEntryPoint = pathToFileURL(internalFile).href;
227
+ }
198
228
  }
199
229
 
200
230
  if (moduleEntryPoint) {
201
- const loadedModule = await importModule(moduleEntryPoint);
231
+ const loadedModule = await importAndPrepareModule(moduleEntryPoint, moduleName);
202
232
  if (loadedModule) {
203
- engine._modules.push(loadedModule);
233
+ loadedModules.push(loadedModule);
204
234
  }
205
235
  } else {
206
- logger.warn(`Aucun point d'entrée trouvé pour le module '${moduleIdentifier}'.`);
236
+ logger.warn(`Could not resolve or find an entry point for module '${moduleIdentifier}'.`);
207
237
  }
208
238
  } catch (e) {
209
- logger.error(`Could not load module '${moduleIdentifier}':`, e.stack);
239
+ logger.error(`Could not load module '${moduleName}' (${moduleIdentifier}):`, e.stack);
240
+ }
241
+ }
242
+
243
+ // Phase 2: Enregistrer et Initialiser tous les modules chargés
244
+ engine._modules = loadedModules; // On enregistre tous les modules dans le moteur
245
+ for (const moduleInstance of engine._modules) {
246
+ if (typeof moduleInstance.onInit === 'function') {
247
+ logger.info(`Initializing module '${moduleInstance.module}'...`);
248
+ await moduleInstance.onInit(engine);
249
+ logger.info(`Module '${moduleInstance.module}' loaded and initialized.`);
210
250
  }
211
251
  }
212
252
 
@@ -239,10 +279,12 @@ export const Engine = {
239
279
  });
240
280
  });
241
281
 
242
- app.use(sirv('client/dist', {
243
- single: true,
244
- dev: process.env.NODE_ENV === 'development'
245
- }));
282
+ if( fs.existsSync('client/dist') ){
283
+ app.use(sirv('client/dist', {
284
+ single: true,
285
+ dev: process.env.NODE_ENV === 'development'
286
+ }));
287
+ }
246
288
 
247
289
  process.on('uncaughtException', function (exception) {
248
290
  console.error(exception);
@@ -277,9 +319,9 @@ export const Engine = {
277
319
  model.locked = true;
278
320
  const r = await createModel(model);
279
321
  dbModels.push({...model, _id: r.insertedId });
280
- logger.info('Model inserted (' + model.name + ')');
322
+ logger.info(`Model ${model.name} inserted.`);
281
323
  }else
282
- logger.info('Model loaded (' + model.name + ')');
324
+ logger.info(`Model ${model.name} loaded`);
283
325
  }
284
326
  logger.info("All models loaded.");
285
327
  await Event.Trigger("OnModelsLoaded", "event", "system", engine, dbModels);
package/src/events.js CHANGED
@@ -1,113 +1,137 @@
1
- import {safeAssignObject} from "./core.js";
2
-
3
- const events = {};
4
-
5
- const eventLayerSystems = {
6
- "priority": ["high", "medium", "low"],
7
- "log": ["info", "debug", "warn", "error", "critical"],
8
- "system": ["calls", "users"],
9
- "event": ["system","user"],
10
- "custom": ["data"]
11
- };
12
-
13
-
14
-
15
- export const Event = {
16
- Trigger: async (name, system = "priority", layer = "medium", ...args) => { // Ajout des arguments system et layer
17
- if (!name || typeof name !== 'string') {
18
- throw new Error('Event name must be a non-empty string');
19
- }
20
-
21
- if (!events[system] || !events[system][name] || (layer && !events[system][name][layer])) {
22
- //console.warn(`No trigger found for ${name} in system ${system} layer ${layer}`);
23
- return null;
24
- }
25
-
26
- const systemsToProcess = system ? [system] : Object.keys(events); // Si system est spécifié, on cible ce système uniquement, sinon tous les systèmes
27
- console.log(`[Event] Triggering ${system}.${layer}.${name}`, {
28
- callbacks: events[system]?.[name]?.[layer]?.length || 0
29
- });
30
- let ret = null;
31
- for (const currentSystem of systemsToProcess) {
32
- if (events[currentSystem] && events[currentSystem][name]) {
33
- const layersToProcess = layer ? [layer] : eventLayerSystems[currentSystem] || Object.keys(events[currentSystem][name]); // Si layer est spécifié, on cible cette couche, sinon toutes les couches ou celles définies dans eventLayerSystems
34
-
35
- if (layersToProcess) {
36
- for (const currentLayer of layersToProcess) {
37
- if (events[currentSystem][name][currentLayer]) {
38
- for (const callback of events[currentSystem][name][currentLayer]) {
39
- try {
40
- const res = await callback(...args);
41
- if (typeof res === "object" && !Array.isArray(res)) {
42
- if (typeof ret !== "object") ret = {};
43
- ret = {...ret, ...res};
44
- } else if (Array.isArray(res)) {
45
- if (!ret || !Array.isArray(ret)) ret = [];
46
- ret = ret.concat(res);
47
- } else if (typeof res === "string") {
48
- if (typeof ret !== "string") ret = "";
49
- ret += res;
50
- } else if (typeof res === "number") {
51
- if (typeof ret !== "number") ret = 0;
52
- ret += res;
53
- } else if (typeof res === "boolean") {
54
- if (typeof ret !== "boolean") ret = true;
55
- ret = res && ret;
56
- } else {
57
- ret = res || ret;
58
- }
59
- } catch (error) {
60
- const errorMsg = `Error in callback for event ${name} in system ${currentSystem} layer ${currentLayer}: ${error.message}`;
61
- const newError = new Error(errorMsg);
62
- newError.originalError = error; // Conserve l'erreur originale
63
- newError.eventDetails = { name, system: currentSystem, layer: currentLayer };
64
- throw newError;
65
- }
66
- }
67
- }
68
- }
69
- }
70
- }
71
- }
72
- return ret;
73
- },
74
- Listen: (name = "", callback, system = "priority", layer = "medium") => {
75
- const validSystems = Object.keys(eventLayerSystems); // Récupération des clés pour une vérification plus performante
76
- if (!validSystems.includes(system)) {
77
- throw new Error(`System '${system}' does not exist. Valid systems are: ${validSystems.join(', ')}`); // Message d'erreur plus informatif
78
- }
79
-
80
- const validLayers = eventLayerSystems[system]; // Récupération des couches valides pour le système
81
- if (!validLayers.includes(layer)) {
82
- throw new Error(`Layer '${layer}' does not exist in system '${system}'. Valid layers are: ${validLayers.join(', ')}`); // Message d'erreur plus informatif
83
- }
84
-
85
- safeAssignObject(events, system, events[system] || {}); // Simplification de la création des objets
86
- safeAssignObject(events[system], name, events[system][name] || {});
87
- safeAssignObject(events[system][name], layer, events[system][name][layer] || []);
88
- events[system][name][layer].push(callback);
89
- },
90
- RemoveCallback: (name, callback, layer="medium", system='priority') => {
91
- if (!events[system] || !events[system][name] || !events[system][name][layer]) {
92
- return; // Si l'événement, le système ou la couche n'existent pas, on ne fait rien
93
- }
94
-
95
- const index = events[system][name][layer].indexOf(callback);
96
- if (index > -1) {
97
- events[system][name][layer].splice(index, 1);
98
- }
99
- },
100
- // Ajout d'une méthode pour supprimer tous les callbacks d'un événement (optionnel)
101
- RemoveAllCallbacks: (name, system = 'priority') => {
102
- if (events[system] && events[system][name]) {
103
- delete events[system][name]; // Supprime l'objet contenant les couches et les callbacks
104
- }
105
- },
106
-
107
- // Ajout d'une méthode pour supprimer tous les callbacks d'un système (optionnel)
108
- RemoveSystemCallbacks: (system) => {
109
- if (events[system]) {
110
- delete events[system];
111
- }
112
- }
113
- }
1
+ import {safeAssignObject} from "./core.js";
2
+
3
+ const events = {};
4
+
5
+ const eventLayerSystems = {
6
+ "priority": ["high", "medium", "low"],
7
+ "log": ["info", "debug", "warn", "error", "critical"],
8
+ "system": ["calls", "users"],
9
+ "event": ["system","user"],
10
+ "custom": ["data"]
11
+ };
12
+
13
+
14
+
15
+ export const Event = {
16
+ Trigger: async (name, system = "priority", layer = "medium", ...args) => { // Ajout des arguments system et layer
17
+ if (!name || typeof name !== 'string') {
18
+ throw new Error('Event name must be a non-empty string');
19
+ }
20
+
21
+ if (!events[system] || !events[system][name] || (layer && !events[system][name][layer])) {
22
+ //console.warn(`No trigger found for ${name} in system ${system} layer ${layer}`);
23
+ return null;
24
+ }
25
+
26
+ const systemsToProcess = system ? [system] : Object.keys(events); // Si system est spécifié, on cible ce système uniquement, sinon tous les systèmes
27
+ console.log(`[Event] Triggering ${system}.${layer}.${name}`, {
28
+ callbacks: events[system]?.[name]?.[layer]?.length || 0
29
+ });
30
+ let ret = null;
31
+ for (const currentSystem of systemsToProcess) {
32
+ if (events[currentSystem] && events[currentSystem][name]) {
33
+ const layersToProcess = layer ? [layer] : eventLayerSystems[currentSystem] || Object.keys(events[currentSystem][name]); // Si layer est spécifié, on cible cette couche, sinon toutes les couches ou celles définies dans eventLayerSystems
34
+
35
+ if (layersToProcess) {
36
+ for (const currentLayer of layersToProcess) {
37
+ if (events[currentSystem][name][currentLayer]) {
38
+ for (const callback of events[currentSystem][name][currentLayer]) {
39
+ try {
40
+ const res = await callback(...args);
41
+ if (typeof res === "object" && !Array.isArray(res)) {
42
+ if (typeof ret !== "object") ret = {};
43
+ ret = {...ret, ...res};
44
+ } else if (Array.isArray(res)) {
45
+ if (!ret || !Array.isArray(ret)) ret = [];
46
+ ret = ret.concat(res);
47
+ } else if (typeof res === "string") {
48
+ if (typeof ret !== "string") ret = "";
49
+ ret += res;
50
+ } else if (typeof res === "number") {
51
+ if (typeof ret !== "number") ret = 0;
52
+ ret += res;
53
+ } else if (typeof res === "boolean") {
54
+ if (typeof ret !== "boolean") ret = true;
55
+ ret = res && ret;
56
+ } else {
57
+ ret = res || ret;
58
+ }
59
+ } catch (error) {
60
+ const errorMsg = `Error in callback for event ${name} in system ${currentSystem} layer ${currentLayer}: ${error.message}`;
61
+ const newError = new Error(errorMsg);
62
+ newError.originalError = error; // Conserve l'erreur originale
63
+ newError.eventDetails = { name, system: currentSystem, layer: currentLayer };
64
+ throw newError;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return ret;
73
+ },
74
+ Listen: (name = "", callback, system = "priority", layer = "medium") => {
75
+ const validSystems = Object.keys(eventLayerSystems); // Récupération des clés pour une vérification plus performante
76
+ if (!validSystems.includes(system)) {
77
+ throw new Error(`System '${system}' does not exist. Valid systems are: ${validSystems.join(', ')}`); // Message d'erreur plus informatif
78
+ }
79
+
80
+ const validLayers = eventLayerSystems[system]; // Récupération des couches valides pour le système
81
+ if (!validLayers.includes(layer)) {
82
+ throw new Error(`Layer '${layer}' does not exist in system '${system}'. Valid layers are: ${validLayers.join(', ')}`); // Message d'erreur plus informatif
83
+ }
84
+
85
+ safeAssignObject(events, system, events[system] || {}); // Simplification de la création des objets
86
+ safeAssignObject(events[system], name, events[system][name] || {});
87
+ safeAssignObject(events[system][name], layer, events[system][name][layer] || []);
88
+ events[system][name][layer].push(callback);
89
+ },
90
+ addSystem: (system) => {
91
+ if (typeof system !== 'string' || system.trim() === '') {
92
+ throw new Error('Le nom du système doit être une chaîne de caractères non vide.');
93
+ }
94
+ if (!eventLayerSystems.hasOwnProperty(system)) {
95
+ eventLayerSystems[system] = [];
96
+ } else {
97
+ console.warn(`[Event] Le système '${system}' existe déjà.`);
98
+ }
99
+ },
100
+ addLayer: (system, layer) => {
101
+ if (typeof system !== 'string' || system.trim() === '') {
102
+ throw new Error('Le nom du système doit être une chaîne de caractères non vide.');
103
+ }
104
+ if (typeof layer !== 'string' || layer.trim() === '') {
105
+ throw new Error('Le nom de la couche doit être une chaîne de caractères non vide.');
106
+ }
107
+ if (!eventLayerSystems.hasOwnProperty(system)) {
108
+ throw new Error(`Le système '${system}' n'existe pas. Veuillez l'ajouter avec addSystem('${system}').`);
109
+ }
110
+ if (!eventLayerSystems[system].includes(layer)) {
111
+ eventLayerSystems[system].push(layer);
112
+ }
113
+ },
114
+ RemoveCallback: (name, callback, layer="medium", system='priority') => {
115
+ if (!events[system] || !events[system][name] || !events[system][name][layer]) {
116
+ return; // Si l'événement, le système ou la couche n'existent pas, on ne fait rien
117
+ }
118
+
119
+ const index = events[system][name][layer].indexOf(callback);
120
+ if (index > -1) {
121
+ events[system][name][layer].splice(index, 1);
122
+ }
123
+ },
124
+ // Ajout d'une méthode pour supprimer tous les callbacks d'un événement (optionnel)
125
+ RemoveAllCallbacks: (name, system = 'priority') => {
126
+ if (events[system] && events[system][name]) {
127
+ delete events[system][name]; // Supprime l'objet contenant les couches et les callbacks
128
+ }
129
+ },
130
+
131
+ // Ajout d'une méthode pour supprimer tous les callbacks d'un système (optionnel)
132
+ RemoveSystemCallbacks: (system) => {
133
+ if (events[system]) {
134
+ delete events[system];
135
+ }
136
+ }
137
+ }
package/src/index.js CHANGED
@@ -5,6 +5,9 @@ export { Engine } from './engine.js';
5
5
  export { GameObject, Logger, BenchmarkTool } from './gameObject.js';
6
6
  export { Config } from './config.js';
7
7
  export { event_on, event_trigger, event_off } from './core.js';
8
+ export { Event } from './events.js';
9
+
10
+ export { sendSseToUser } from './modules/data/data.routes.js';
8
11
 
9
12
  export { UserProvider } from './providers.js';
10
13