wu-framework 1.1.12 → 1.1.14
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/dist/wu-framework.cjs.js +1 -1
- package/dist/wu-framework.cjs.js.map +1 -1
- package/dist/wu-framework.dev.js +91 -17
- package/dist/wu-framework.dev.js.map +1 -1
- package/dist/wu-framework.esm.js +1 -1
- package/dist/wu-framework.esm.js.map +1 -1
- package/dist/wu-framework.umd.js +1 -1
- package/dist/wu-framework.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/react/index.js +5 -18
- package/src/core/wu-core.js +91 -17
package/dist/wu-framework.dev.js
CHANGED
|
@@ -7363,6 +7363,8 @@ class WuCore {
|
|
|
7363
7363
|
this.manifests = new Map(); // Manifiestos cargados
|
|
7364
7364
|
this.mounted = new Map(); // Apps montadas
|
|
7365
7365
|
this.hidden = new Map(); // Keep-alive hidden apps
|
|
7366
|
+
this._pendingUnmounts = new Map(); // Deferred unmount timers (StrictMode compat)
|
|
7367
|
+
this._mountingPromises = new Map(); // In-flight mount dedup
|
|
7366
7368
|
|
|
7367
7369
|
// Componentes core
|
|
7368
7370
|
this.loader = new WuLoader();
|
|
@@ -7511,6 +7513,31 @@ class WuCore {
|
|
|
7511
7513
|
* @param {string} containerSelector - Selector del contenedor
|
|
7512
7514
|
*/
|
|
7513
7515
|
async mount(appName, containerSelector) {
|
|
7516
|
+
// ── StrictMode guard: cancel pending deferred unmount ──
|
|
7517
|
+
// React StrictMode cycle: effect(mount) → cleanup(unmount) → effect(mount)
|
|
7518
|
+
// The cleanup fires between two mounts. By deferring the actual unmount,
|
|
7519
|
+
// the second mount cancels it and the app stays alive — zero flicker.
|
|
7520
|
+
if (this._pendingUnmounts.has(appName)) {
|
|
7521
|
+
clearTimeout(this._pendingUnmounts.get(appName));
|
|
7522
|
+
this._pendingUnmounts.delete(appName);
|
|
7523
|
+
logger.wuDebug(`${appName} deferred unmount cancelled by remount`);
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
// Already mounted in same container → no-op
|
|
7527
|
+
if (this.mounted.has(appName)) {
|
|
7528
|
+
const existing = this.mounted.get(appName);
|
|
7529
|
+
if (existing.containerSelector === containerSelector) {
|
|
7530
|
+
logger.wuDebug(`${appName} already mounted in ${containerSelector}`);
|
|
7531
|
+
return;
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7535
|
+
// Deduplicate concurrent mounts (StrictMode fires effect twice)
|
|
7536
|
+
if (this._mountingPromises.has(appName)) {
|
|
7537
|
+
logger.wuDebug(`${appName} mount already in progress, deduplicating`);
|
|
7538
|
+
return await this._mountingPromises.get(appName);
|
|
7539
|
+
}
|
|
7540
|
+
|
|
7514
7541
|
// Check if app is in keep-alive (hidden) state
|
|
7515
7542
|
const hiddenEntry = this.hidden.get(appName);
|
|
7516
7543
|
if (hiddenEntry) {
|
|
@@ -7522,7 +7549,15 @@ class WuCore {
|
|
|
7522
7549
|
await this._destroyHidden(appName);
|
|
7523
7550
|
}
|
|
7524
7551
|
|
|
7525
|
-
|
|
7552
|
+
// Track mount promise for deduplication
|
|
7553
|
+
const mountPromise = this.mountWithRecovery(appName, containerSelector, 0);
|
|
7554
|
+
this._mountingPromises.set(appName, mountPromise);
|
|
7555
|
+
|
|
7556
|
+
try {
|
|
7557
|
+
return await mountPromise;
|
|
7558
|
+
} finally {
|
|
7559
|
+
this._mountingPromises.delete(appName);
|
|
7560
|
+
}
|
|
7526
7561
|
}
|
|
7527
7562
|
|
|
7528
7563
|
/**
|
|
@@ -8173,28 +8208,60 @@ class WuCore {
|
|
|
8173
8208
|
* @param {boolean} [options.force] - Force destroy even if keepAlive
|
|
8174
8209
|
*/
|
|
8175
8210
|
async unmount(appName, options = {}) {
|
|
8176
|
-
|
|
8177
|
-
logger.wuDebug(`Unmounting ${appName}`);
|
|
8211
|
+
logger.wuDebug(`Unmounting ${appName}`);
|
|
8178
8212
|
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
}
|
|
8185
|
-
logger.wuWarn(`App ${appName} not mounted`);
|
|
8186
|
-
return;
|
|
8213
|
+
const mounted = this.mounted.get(appName);
|
|
8214
|
+
if (!mounted) {
|
|
8215
|
+
// Check if it's hidden (keep-alive) — force destroy if requested
|
|
8216
|
+
if (options.force && this.hidden.has(appName)) {
|
|
8217
|
+
return await this._destroyHidden(appName);
|
|
8187
8218
|
}
|
|
8219
|
+
logger.wuWarn(`App ${appName} not mounted`);
|
|
8220
|
+
return;
|
|
8221
|
+
}
|
|
8188
8222
|
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8223
|
+
// Resolve keepAlive: per-call > per-app config > default false
|
|
8224
|
+
const keepAlive = options.force
|
|
8225
|
+
? false
|
|
8226
|
+
: (options.keepAlive ?? mounted.app?.keepAlive ?? false);
|
|
8193
8227
|
|
|
8194
|
-
|
|
8195
|
-
|
|
8228
|
+
if (keepAlive) {
|
|
8229
|
+
return await this.hide(appName);
|
|
8230
|
+
}
|
|
8231
|
+
|
|
8232
|
+
// Force → immediate unmount (no deferral)
|
|
8233
|
+
if (options.force) {
|
|
8234
|
+
return await this._executeUnmount(appName, mounted);
|
|
8235
|
+
}
|
|
8236
|
+
|
|
8237
|
+
// ── Deferred unmount: 60ms window for React StrictMode ──
|
|
8238
|
+
// StrictMode cycle: effect(mount) → cleanup(unmount) → effect(mount)
|
|
8239
|
+
// The cleanup fires between two mounts. By deferring the actual unmount,
|
|
8240
|
+
// the second mount() call cancels the timer and the app stays alive.
|
|
8241
|
+
if (this._pendingUnmounts.has(appName)) {
|
|
8242
|
+
clearTimeout(this._pendingUnmounts.get(appName));
|
|
8243
|
+
}
|
|
8244
|
+
|
|
8245
|
+
this._pendingUnmounts.set(appName, setTimeout(async () => {
|
|
8246
|
+
this._pendingUnmounts.delete(appName);
|
|
8247
|
+
// Re-verify: only unmount if the same mount entry is still current
|
|
8248
|
+
if (this.mounted.has(appName) && this.mounted.get(appName) === mounted) {
|
|
8249
|
+
try {
|
|
8250
|
+
await this._executeUnmount(appName, mounted);
|
|
8251
|
+
} catch (error) {
|
|
8252
|
+
logger.wuError(`Deferred unmount failed for ${appName}:`, error);
|
|
8253
|
+
}
|
|
8196
8254
|
}
|
|
8255
|
+
}, 60));
|
|
8256
|
+
}
|
|
8197
8257
|
|
|
8258
|
+
/**
|
|
8259
|
+
* Execute the actual unmount immediately (no deferral).
|
|
8260
|
+
* Called by the deferred timer, force unmount, or destroy.
|
|
8261
|
+
* @private
|
|
8262
|
+
*/
|
|
8263
|
+
async _executeUnmount(appName, mounted) {
|
|
8264
|
+
try {
|
|
8198
8265
|
// Execute beforeUnmount hooks
|
|
8199
8266
|
const beforeUnmountResult = await this.hooks.execute('beforeUnmount', { appName, mounted });
|
|
8200
8267
|
if (beforeUnmountResult.cancelled) {
|
|
@@ -8610,6 +8677,13 @@ class WuCore {
|
|
|
8610
8677
|
// Call plugin onDestroy hooks
|
|
8611
8678
|
await this.pluginSystem.callHook('onDestroy', {});
|
|
8612
8679
|
|
|
8680
|
+
// Cancel all pending deferred unmounts
|
|
8681
|
+
for (const timer of this._pendingUnmounts.values()) {
|
|
8682
|
+
clearTimeout(timer);
|
|
8683
|
+
}
|
|
8684
|
+
this._pendingUnmounts.clear();
|
|
8685
|
+
this._mountingPromises.clear();
|
|
8686
|
+
|
|
8613
8687
|
// Force-destroy all hidden (keep-alive) apps first
|
|
8614
8688
|
for (const appName of [...this.hidden.keys()]) {
|
|
8615
8689
|
await this._destroyHidden(appName);
|