slicejs-web-framework 2.2.7 → 2.2.8
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/Slice/Components/Structural/Controller/Controller.js +353 -2
- package/Slice/Slice.js +40 -13
- package/api/index.js +80 -34
- package/package.json +1 -1
|
@@ -7,14 +7,365 @@ export default class Controller {
|
|
|
7
7
|
this.classes = new Map();
|
|
8
8
|
this.requestedStyles = new Set(); // ✅ CRÍTICO: Para tracking de CSS cargados
|
|
9
9
|
this.activeComponents = new Map();
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
// 🚀 OPTIMIZACIÓN: Índice inverso para búsqueda rápida de hijos
|
|
12
12
|
// parentSliceId → Set<childSliceId>
|
|
13
13
|
this.childrenIndex = new Map();
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
// 📦 Bundle system
|
|
16
|
+
this.loadedBundles = new Set();
|
|
17
|
+
this.bundleConfig = null;
|
|
18
|
+
this.criticalBundleLoaded = false;
|
|
19
|
+
|
|
15
20
|
this.idCounter = 0;
|
|
16
21
|
}
|
|
17
22
|
|
|
23
|
+
/**
|
|
24
|
+
* 📦 Initializes bundle system (called automatically when config is loaded)
|
|
25
|
+
*/
|
|
26
|
+
initializeBundles(config = null) {
|
|
27
|
+
if (config) {
|
|
28
|
+
this.bundleConfig = config;
|
|
29
|
+
|
|
30
|
+
// Register critical bundle components if available
|
|
31
|
+
if (config.bundles?.critical) {
|
|
32
|
+
// The critical bundle should already be loaded, register its components
|
|
33
|
+
this.loadedBundles.add('critical');
|
|
34
|
+
// Note: Critical bundle registration is handled by the auto-import
|
|
35
|
+
}
|
|
36
|
+
this.criticalBundleLoaded = true;
|
|
37
|
+
} else {
|
|
38
|
+
// No bundles available, will use individual component loading
|
|
39
|
+
this.bundleConfig = null;
|
|
40
|
+
this.criticalBundleLoaded = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 📦 Loads a bundle by name or category
|
|
46
|
+
*/
|
|
47
|
+
async loadBundle(bundleName) {
|
|
48
|
+
if (this.loadedBundles.has(bundleName)) {
|
|
49
|
+
return; // Already loaded
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const bundleInfo = this.bundleConfig?.bundles?.routes?.[bundleName];
|
|
54
|
+
|
|
55
|
+
if (!bundleInfo) {
|
|
56
|
+
console.warn(`Bundle ${bundleName} not found in configuration`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const bundlePath = `/bundles/${bundleInfo.file}`;
|
|
61
|
+
|
|
62
|
+
// Dynamic import of the bundle
|
|
63
|
+
const bundleModule = await import(bundlePath);
|
|
64
|
+
|
|
65
|
+
// Manually register components from the imported bundle
|
|
66
|
+
if (bundleModule.SLICE_BUNDLE) {
|
|
67
|
+
this.registerBundle(bundleModule.SLICE_BUNDLE);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.loadedBundles.add(bundleName);
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.warn(`Failed to load bundle ${bundleName}:`, error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 📦 Registers a bundle's components (called automatically by bundle files)
|
|
79
|
+
*/
|
|
80
|
+
registerBundleLegacy(bundle) {
|
|
81
|
+
const { components, metadata } = bundle;
|
|
82
|
+
|
|
83
|
+
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
84
|
+
|
|
85
|
+
// Phase 1: Register templates and CSS for all components first
|
|
86
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
87
|
+
try {
|
|
88
|
+
// Register HTML template
|
|
89
|
+
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
90
|
+
const template = document.createElement('template');
|
|
91
|
+
template.innerHTML = componentData.html || '';
|
|
92
|
+
this.templates.set(componentName, template);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Register CSS styles
|
|
96
|
+
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
97
|
+
// Use the existing stylesManager to register component styles
|
|
98
|
+
if (window.slice && window.slice.stylesManager) {
|
|
99
|
+
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
100
|
+
this.requestedStyles.add(componentName);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.warn(`❌ Failed to register assets for ${componentName}:`, error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Phase 2: Evaluate all external file dependencies
|
|
109
|
+
const processedDeps = new Set();
|
|
110
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
111
|
+
if (componentData.dependencies) {
|
|
112
|
+
for (const [depName, depContent] of Object.entries(componentData.dependencies)) {
|
|
113
|
+
if (!processedDeps.has(depName)) {
|
|
114
|
+
try {
|
|
115
|
+
// Convert ES6 exports to global assignments
|
|
116
|
+
let processedContent = depContent
|
|
117
|
+
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
118
|
+
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
119
|
+
.replace(/export\s+var\s+(\w+)\s*=/g, 'window.$1 =')
|
|
120
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
121
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
122
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
123
|
+
return exports.split(',').map(exp => {
|
|
124
|
+
const cleanExp = exp.trim();
|
|
125
|
+
const varName = cleanExp.split(' as ')[0].trim();
|
|
126
|
+
return `window.${varName} = ${varName};`;
|
|
127
|
+
}).join('\n');
|
|
128
|
+
})
|
|
129
|
+
// Remove any remaining export keywords
|
|
130
|
+
.replace(/^\s*export\s+/gm, '');
|
|
131
|
+
|
|
132
|
+
// Evaluate the dependency
|
|
133
|
+
try {
|
|
134
|
+
new Function('slice', 'customElements', 'window', 'document', processedContent)
|
|
135
|
+
(window.slice, window.customElements, window, window.document);
|
|
136
|
+
} catch (evalError) {
|
|
137
|
+
console.warn(`❌ Failed to evaluate processed dependency ${depName}:`, evalError);
|
|
138
|
+
console.warn('Processed content preview:', processedContent.substring(0, 200));
|
|
139
|
+
// Try evaluating the original content as fallback
|
|
140
|
+
try {
|
|
141
|
+
new Function('slice', 'customElements', 'window', 'document', depContent)
|
|
142
|
+
(window.slice, window.customElements, window, window.document);
|
|
143
|
+
console.log(`✅ Fallback evaluation succeeded for ${depName}`);
|
|
144
|
+
} catch (fallbackError) {
|
|
145
|
+
console.warn(`❌ Fallback evaluation also failed for ${depName}:`, fallbackError);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
processedDeps.add(depName);
|
|
150
|
+
console.log(`📄 Dependency loaded: ${depName}`);
|
|
151
|
+
} catch (depError) {
|
|
152
|
+
console.warn(`⚠️ Failed to load dependency ${depName} for ${componentName}:`, depError);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Phase 3: Evaluate all component classes (now that dependencies are available)
|
|
160
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
161
|
+
// For JavaScript classes, we need to evaluate the code
|
|
162
|
+
if (componentData.js && !this.classes.has(componentName)) {
|
|
163
|
+
try {
|
|
164
|
+
// Create evaluation context with dependencies
|
|
165
|
+
let evalCode = componentData.js;
|
|
166
|
+
|
|
167
|
+
// Prepend dependencies to make them available
|
|
168
|
+
if (componentData.dependencies) {
|
|
169
|
+
const depCode = Object.entries(componentData.dependencies)
|
|
170
|
+
.map(([depName, depContent]) => {
|
|
171
|
+
// Convert ES6 exports to global assignments
|
|
172
|
+
return depContent
|
|
173
|
+
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
174
|
+
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
175
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
176
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
177
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
178
|
+
return exports.split(',').map(exp => {
|
|
179
|
+
const cleanExp = exp.trim();
|
|
180
|
+
return `window.${cleanExp} = ${cleanExp};`;
|
|
181
|
+
}).join('\n');
|
|
182
|
+
});
|
|
183
|
+
})
|
|
184
|
+
.join('\n\n');
|
|
185
|
+
|
|
186
|
+
evalCode = depCode + '\n\n' + evalCode;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Evaluate the complete code
|
|
190
|
+
const componentClass = new Function('slice', 'customElements', 'window', 'document', `
|
|
191
|
+
"use strict";
|
|
192
|
+
${evalCode}
|
|
193
|
+
return ${componentName};
|
|
194
|
+
`)(window.slice, window.customElements, window, window.document);
|
|
195
|
+
|
|
196
|
+
if (componentClass) {
|
|
197
|
+
this.classes.set(componentName, componentClass);
|
|
198
|
+
console.log(`📝 Class registered for: ${componentName}`);
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.warn(`❌ Failed to evaluate class for ${componentName}:`, error);
|
|
202
|
+
console.warn('Code that failed:', componentData.js.substring(0, 200) + '...');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 📦 New bundle registration method (simplified and robust)
|
|
212
|
+
*/
|
|
213
|
+
registerBundle(bundle) {
|
|
214
|
+
const { components, metadata } = bundle;
|
|
215
|
+
|
|
216
|
+
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
217
|
+
|
|
218
|
+
// Phase 1: Register all templates and CSS first
|
|
219
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
220
|
+
try {
|
|
221
|
+
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
222
|
+
const template = document.createElement('template');
|
|
223
|
+
template.innerHTML = componentData.html || '';
|
|
224
|
+
this.templates.set(componentName, template);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
228
|
+
if (window.slice && window.slice.stylesManager) {
|
|
229
|
+
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
230
|
+
this.requestedStyles.add(componentName);
|
|
231
|
+
console.log(`🎨 CSS registered for: ${componentName}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.warn(`❌ Failed to register assets for ${componentName}:`, error);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Phase 2: Evaluate all external file dependencies first
|
|
240
|
+
const processedDeps = new Set();
|
|
241
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
242
|
+
if (componentData.externalDependencies) {
|
|
243
|
+
for (const [depName, depContent] of Object.entries(componentData.externalDependencies)) {
|
|
244
|
+
if (!processedDeps.has(depName)) {
|
|
245
|
+
try {
|
|
246
|
+
// Process ES6 exports to make the code evaluable
|
|
247
|
+
let processedContent = depContent
|
|
248
|
+
// Convert named exports: export const varName = ... → window.varName = ...
|
|
249
|
+
.replace(/export\s+const\s+(\w+)\s*=\s*/g, 'window.$1 = ')
|
|
250
|
+
.replace(/export\s+let\s+(\w+)\s*=\s*/g, 'window.$1 = ')
|
|
251
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
252
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport = ')
|
|
253
|
+
// Handle export { var1, var2 } statements
|
|
254
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exportsStr) => {
|
|
255
|
+
const exports = exportsStr.split(',').map(exp => exp.trim().split(' as ')[0].trim());
|
|
256
|
+
return exports.map(varName => `window.${varName} = ${varName};`).join('\n');
|
|
257
|
+
})
|
|
258
|
+
// Remove any remaining export keywords
|
|
259
|
+
.replace(/^\s*export\s+/gm, '');
|
|
260
|
+
|
|
261
|
+
// Evaluate the processed content
|
|
262
|
+
new Function('slice', 'customElements', 'window', 'document', processedContent)
|
|
263
|
+
(window.slice, window.customElements, window, window.document);
|
|
264
|
+
|
|
265
|
+
processedDeps.add(depName);
|
|
266
|
+
console.log(`📄 External dependency loaded: ${depName}`);
|
|
267
|
+
} catch (depError) {
|
|
268
|
+
console.warn(`⚠️ Failed to load external dependency ${depName}:`, depError);
|
|
269
|
+
console.warn('Original content preview:', depContent.substring(0, 200));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Phase 3: Evaluate all component classes (external dependencies are now available)
|
|
277
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
278
|
+
if (componentData.js && !this.classes.has(componentName)) {
|
|
279
|
+
try {
|
|
280
|
+
// Simple evaluation
|
|
281
|
+
const componentClass = new Function('slice', 'customElements', 'window', 'document', `
|
|
282
|
+
${componentData.js}
|
|
283
|
+
return ${componentName};
|
|
284
|
+
`)(window.slice, window.customElements, window, window.document);
|
|
285
|
+
|
|
286
|
+
if (componentClass) {
|
|
287
|
+
this.classes.set(componentName, componentClass);
|
|
288
|
+
console.log(`📝 Class registered for: ${componentName}`);
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.warn(`❌ Failed to evaluate class for ${componentName}:`, error);
|
|
292
|
+
// Continue with other components instead of failing completely
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
console.log(`✅ Bundle registration completed: ${metadata.componentCount} components processed`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 📦 Determines which bundle to load for a component
|
|
302
|
+
*/
|
|
303
|
+
getBundleForComponent(componentName) {
|
|
304
|
+
if (!this.bundleConfig?.bundles) return null;
|
|
305
|
+
|
|
306
|
+
// Check if component is in critical bundle
|
|
307
|
+
if (this.bundleConfig.bundles.critical?.components?.includes(componentName)) {
|
|
308
|
+
return 'critical';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Find component in route bundles
|
|
312
|
+
if (this.bundleConfig.bundles.routes) {
|
|
313
|
+
for (const [bundleName, bundleInfo] of Object.entries(this.bundleConfig.bundles.routes)) {
|
|
314
|
+
if (bundleInfo.components?.includes(componentName)) {
|
|
315
|
+
return bundleName;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* 📦 Checks if a component is available from loaded bundles
|
|
325
|
+
*/
|
|
326
|
+
isComponentFromBundle(componentName) {
|
|
327
|
+
if (!this.bundleConfig?.bundles) return false;
|
|
328
|
+
|
|
329
|
+
// Check critical bundle
|
|
330
|
+
if (this.bundleConfig.bundles.critical?.components?.includes(componentName)) {
|
|
331
|
+
return this.criticalBundleLoaded;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Check route bundles
|
|
335
|
+
if (this.bundleConfig.bundles.routes) {
|
|
336
|
+
for (const [bundleName, bundleInfo] of Object.entries(this.bundleConfig.bundles.routes)) {
|
|
337
|
+
if (bundleInfo.components?.includes(componentName)) {
|
|
338
|
+
return this.loadedBundles.has(bundleName);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* 📦 Gets component data from loaded bundles
|
|
348
|
+
*/
|
|
349
|
+
getComponentFromBundle(componentName) {
|
|
350
|
+
if (!this.bundleConfig?.bundles) return null;
|
|
351
|
+
|
|
352
|
+
// Find component in any loaded bundle
|
|
353
|
+
const allBundles = [
|
|
354
|
+
{ name: 'critical', data: this.bundleConfig.bundles.critical },
|
|
355
|
+
...Object.entries(this.bundleConfig.bundles.routes || {}).map(([name, data]) => ({ name, data }))
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
for (const { name: bundleName, data: bundleData } of allBundles) {
|
|
359
|
+
if (bundleData?.components?.includes(componentName) && this.loadedBundles.has(bundleName)) {
|
|
360
|
+
// Find the bundle file and extract component data
|
|
361
|
+
// This is a simplified version - in practice you'd need to access the loaded bundle data
|
|
362
|
+
return { bundleName, componentName };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
18
369
|
logActiveComponents() {
|
|
19
370
|
this.activeComponents.forEach((component) => {
|
|
20
371
|
let parent = component.parentComponent;
|
package/Slice/Slice.js
CHANGED
|
@@ -11,6 +11,8 @@ export default class Slice {
|
|
|
11
11
|
this.loggerConfig = sliceConfig.logger;
|
|
12
12
|
this.debuggerConfig = sliceConfig.debugger;
|
|
13
13
|
this.loadingConfig = sliceConfig.loading;
|
|
14
|
+
|
|
15
|
+
// 📦 Bundle system is initialized automatically via import in index.js
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
async getClass(module) {
|
|
@@ -46,8 +48,17 @@ export default class Slice {
|
|
|
46
48
|
return null;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
// 📦 Try to load from bundles first
|
|
52
|
+
const bundleName = this.controller.getBundleForComponent(componentName);
|
|
53
|
+
if (bundleName && !this.controller.loadedBundles.has(bundleName)) {
|
|
54
|
+
await this.controller.loadBundle(bundleName);
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
let componentCategory = this.controller.componentCategories.get(componentName);
|
|
50
58
|
|
|
59
|
+
// 📦 Check if component is already available from loaded bundles
|
|
60
|
+
const isFromBundle = this.controller.isComponentFromBundle(componentName);
|
|
61
|
+
|
|
51
62
|
if (componentCategory === 'Structural') {
|
|
52
63
|
this.logger.logError(
|
|
53
64
|
'Slice',
|
|
@@ -57,27 +68,31 @@ export default class Slice {
|
|
|
57
68
|
return null;
|
|
58
69
|
}
|
|
59
70
|
|
|
60
|
-
let isVisual = slice.paths.components[componentCategory].type === "Visual";
|
|
71
|
+
let isVisual = slice.paths.components[componentCategory].type === "Visual";
|
|
61
72
|
let modulePath = `${slice.paths.components[componentCategory].path}/${componentName}/${componentName}.js`;
|
|
62
73
|
|
|
63
74
|
// Load template, class, and CSS concurrently if needed
|
|
64
75
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
// 📦 Skip individual loading if component is available from bundles
|
|
77
|
+
const loadTemplate = (isFromBundle || !isVisual || this.controller.templates.has(componentName))
|
|
78
|
+
? Promise.resolve(null)
|
|
79
|
+
: this.controller.fetchText(componentName, 'html', componentCategory);
|
|
69
80
|
|
|
70
|
-
const loadClass =
|
|
71
|
-
?
|
|
72
|
-
:
|
|
81
|
+
const loadClass = (isFromBundle || this.controller.classes.has(componentName))
|
|
82
|
+
? Promise.resolve(null)
|
|
83
|
+
: this.getClass(modulePath);
|
|
73
84
|
|
|
74
|
-
const loadCSS =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
: Promise.resolve(null);
|
|
85
|
+
const loadCSS = (isFromBundle || !isVisual || this.controller.requestedStyles.has(componentName))
|
|
86
|
+
? Promise.resolve(null)
|
|
87
|
+
: this.controller.fetchText(componentName, 'css', componentCategory);
|
|
78
88
|
|
|
79
89
|
const [html, ComponentClass, css] = await Promise.all([loadTemplate, loadClass, loadCSS]);
|
|
80
90
|
|
|
91
|
+
// 📦 If component is from bundle but not in cache, it should have been registered by registerBundle
|
|
92
|
+
if (isFromBundle) {
|
|
93
|
+
console.log(`📦 Using bundled component: ${componentName}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
if (html || html === '') {
|
|
82
97
|
const template = document.createElement('template');
|
|
83
98
|
template.innerHTML = html;
|
|
@@ -219,4 +234,16 @@ async function init() {
|
|
|
219
234
|
|
|
220
235
|
}
|
|
221
236
|
|
|
222
|
-
await init();
|
|
237
|
+
await init();
|
|
238
|
+
|
|
239
|
+
// Initialize bundles if available
|
|
240
|
+
try {
|
|
241
|
+
const { initializeBundles } = await import('/bundles/bundle.config.js');
|
|
242
|
+
if (initializeBundles) {
|
|
243
|
+
await initializeBundles(window.slice);
|
|
244
|
+
console.log('📦 Bundles initialized automatically');
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
// Bundles not available, continue with individual component loading
|
|
248
|
+
console.log('📄 Using individual component loading (no bundles found)');
|
|
249
|
+
}
|
package/api/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// api/index.js - Seguridad automática sin configuración
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
import { dirname } from 'path';
|
|
6
7
|
import {
|
|
@@ -60,6 +61,15 @@ app.use(securityMiddleware({
|
|
|
60
61
|
// MIDDLEWARES DE APLICACIÓN
|
|
61
62
|
// ==============================================
|
|
62
63
|
|
|
64
|
+
// Middleware global para archivos JavaScript con MIME types correctos
|
|
65
|
+
app.use((req, res, next) => {
|
|
66
|
+
if (req.path.endsWith('.js')) {
|
|
67
|
+
// Forzar headers correctos para TODOS los archivos .js
|
|
68
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
69
|
+
}
|
|
70
|
+
next();
|
|
71
|
+
});
|
|
72
|
+
|
|
63
73
|
// Middleware para parsear JSON y formularios
|
|
64
74
|
app.use(express.json());
|
|
65
75
|
app.use(express.urlencoded({ extended: true }));
|
|
@@ -81,6 +91,76 @@ app.use((req, res, next) => {
|
|
|
81
91
|
// ARCHIVOS ESTÁTICOS (DESPUÉS DE SEGURIDAD)
|
|
82
92
|
// ==============================================
|
|
83
93
|
|
|
94
|
+
// Función de utilidad para verificar si existe el directorio bundles
|
|
95
|
+
function bundlesDirectoryExists() {
|
|
96
|
+
const bundleDir = path.join(__dirname, `../${folderDeployed}`, 'bundles');
|
|
97
|
+
return fs.existsSync(bundleDir) && fs.statSync(bundleDir).isDirectory();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Capturar todas las peticiones a bundles para debugging
|
|
101
|
+
app.use('/bundles/', (req, res, next) => {
|
|
102
|
+
console.log(`🔍 Bundle request: ${req.method} ${req.originalUrl}`);
|
|
103
|
+
next();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Middleware personalizado para archivos de bundles con MIME types correctos
|
|
107
|
+
// ⚠️ DEBE IR ANTES del middleware general para tener prioridad
|
|
108
|
+
app.use('/bundles/', (req, res, next) => {
|
|
109
|
+
// Verificar si existe el directorio bundles
|
|
110
|
+
if (!bundlesDirectoryExists()) {
|
|
111
|
+
console.log(`ℹ️ Bundles directory does not exist, skipping bundle processing`);
|
|
112
|
+
return next(); // Continuar con el siguiente middleware
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Solo procesar archivos .js
|
|
116
|
+
if (req.path.endsWith('.js')) {
|
|
117
|
+
const filePath = path.join(__dirname, `../${folderDeployed}`, 'bundles', req.path);
|
|
118
|
+
console.log(`📂 Processing bundle: ${req.path} -> ${filePath}`);
|
|
119
|
+
|
|
120
|
+
// Verificar que el archivo existe
|
|
121
|
+
if (fs.existsSync(filePath)) {
|
|
122
|
+
try {
|
|
123
|
+
// Leer y servir el archivo con headers correctos
|
|
124
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
125
|
+
res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
|
|
126
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); // No cachear para permitir actualizaciones en tiempo real
|
|
127
|
+
res.setHeader('Pragma', 'no-cache');
|
|
128
|
+
res.setHeader('Expires', '0');
|
|
129
|
+
console.log(`✅ Serving bundle: ${req.path} (${fileContent.length} bytes, ${Buffer.byteLength(fileContent, 'utf8')} bytes UTF-8)`);
|
|
130
|
+
return res.send(fileContent);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.log(`❌ Error reading bundle file: ${error.message}`);
|
|
133
|
+
return res.status(500).send('Error reading bundle file');
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
console.log(`❌ Bundle file not found: ${filePath}`);
|
|
137
|
+
// Listar archivos disponibles para debugging
|
|
138
|
+
try {
|
|
139
|
+
const bundleDir = path.join(__dirname, `../${folderDeployed}`, 'bundles');
|
|
140
|
+
if (fs.existsSync(bundleDir)) {
|
|
141
|
+
const files = fs.readdirSync(bundleDir);
|
|
142
|
+
console.log(`📁 Available files in bundles: ${files.join(', ')}`);
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
console.log(`❌ Could not list bundle directory: ${e.message}`);
|
|
146
|
+
}
|
|
147
|
+
return res.status(404).send('Bundle file not found');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Para archivos no .js, continuar con el middleware estático normal
|
|
152
|
+
next();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Servir otros archivos de bundles (CSS, etc.) con el middleware estático normal
|
|
156
|
+
// Solo si existe el directorio bundles
|
|
157
|
+
if (bundlesDirectoryExists()) {
|
|
158
|
+
app.use('/bundles/', express.static(path.join(__dirname, `../${folderDeployed}`, 'bundles')));
|
|
159
|
+
console.log(`📦 Bundles directory found, serving static files`);
|
|
160
|
+
} else {
|
|
161
|
+
console.log(`ℹ️ Bundles directory not found, skipping static bundle serving`);
|
|
162
|
+
}
|
|
163
|
+
|
|
84
164
|
// Servir framework Slice.js
|
|
85
165
|
app.use('/Slice/', express.static(path.join(__dirname, '..', 'node_modules', 'slicejs-web-framework', 'Slice')));
|
|
86
166
|
|
|
@@ -108,40 +188,6 @@ app.get('/api/status', (req, res) => {
|
|
|
108
188
|
});
|
|
109
189
|
});
|
|
110
190
|
|
|
111
|
-
// Ruta para verificar estado de seguridad
|
|
112
|
-
app.get('/api/security-status', (req, res) => {
|
|
113
|
-
const host = req.get('Host');
|
|
114
|
-
|
|
115
|
-
res.json({
|
|
116
|
-
frameworkProtection: {
|
|
117
|
-
status: 'active',
|
|
118
|
-
mode: 'automatic',
|
|
119
|
-
description: 'Allows framework file loading from application, blocks direct browser access',
|
|
120
|
-
detectedHost: host
|
|
121
|
-
},
|
|
122
|
-
blockedPaths: [
|
|
123
|
-
'/node_modules/*',
|
|
124
|
-
'/package.json',
|
|
125
|
-
'/.env',
|
|
126
|
-
'/.git/*'
|
|
127
|
-
],
|
|
128
|
-
protectedPaths: {
|
|
129
|
-
directAccessBlocked: [
|
|
130
|
-
'/Slice/Components/Structural/*',
|
|
131
|
-
'/Slice/Core/*',
|
|
132
|
-
'/Slice/Services/*'
|
|
133
|
-
],
|
|
134
|
-
allowedFrom: 'Any request with valid Referer matching current host'
|
|
135
|
-
},
|
|
136
|
-
howItWorks: {
|
|
137
|
-
automatic: true,
|
|
138
|
-
config: 'No configuration needed',
|
|
139
|
-
localhost: 'Works automatically',
|
|
140
|
-
customDomain: 'Works automatically',
|
|
141
|
-
detection: 'Uses Referer and Host headers'
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
191
|
|
|
146
192
|
// ==============================================
|
|
147
193
|
// SPA FALLBACK
|