aril 2.0.1-dev.1 → 2.0.1-dev.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.
@@ -627,6 +627,13 @@ class CustomRouteReuseStrategy {
627
627
  return false;
628
628
  }
629
629
  const routeKey = this.getRouteKey(route);
630
+ // Angular `outlet.detach()`, `store()`'dan ÖNCE çalışıp wrapper view'ını DOM'dan söker →
631
+ // içindeki MFE `<app-xxx>` element'i disconnect olur ve barındırdığı iframe (örn. Superset embed)
632
+ // reload olup auth handshake'ini kalıcı kaybeder. Bu yüzden element'i HÂLÂ bağlıyken, detach'ten
633
+ // ÖNCE atomic `moveBefore` ile hidden container'a alıyoruz (bkz. preserveLiveElementBeforeDetach).
634
+ if (this.isMatcherRoute(route)) {
635
+ this.preserveLiveElementBeforeDetach(route);
636
+ }
630
637
  console.log(`[Strategy] shouldDetach key="${routeKey}" → true`);
631
638
  return true;
632
639
  }
@@ -686,18 +693,16 @@ class CustomRouteReuseStrategy {
686
693
  return;
687
694
  const element = componentInstance.element;
688
695
  const hiddenContainer = this.getHiddenContainer();
689
- // Flag — disconnectedCallback bunu okuyup destroy'ı atlayacak. removeChild'tan
690
- // ÖNCE set ediliyor ki callback ilk tetiklendiğinde flag yerinde olsun.
696
+ // Flag — fallback (moveBefore desteklenmeyen tarayıcı) removeChild yaptığında disconnectedCallback
697
+ // bunu okuyup destroy'ı atlar. moveBefore yolunda callback hiç tetiklenmez (flag zararsız kalır).
691
698
  element.__arilPreserveDuringDetach = true;
692
- // Eğer element zaten bir parent'a sahipse, ondan ayır
693
- if (element.parentNode && element.parentNode !== hiddenContainer) {
694
- element.parentNode.removeChild(element);
695
- }
696
- // Hidden container'a ekle (eğer zaten orada değilse)
697
- if (!hiddenContainer.contains(element)) {
698
- hiddenContainer.appendChild(element);
699
- console.log('Web component element hidden container\'a taşındı (state korunuyor)');
700
- }
699
+ // `shouldDetach` çoğu durumda element'i detach'ten ÖNCE zaten taşıdı burada no-op.
700
+ if (hiddenContainer.contains(element))
701
+ return;
702
+ // Taşınmadıysa (ör. shouldDetach element'i bulamadı): bu noktada element Angular tarafından
703
+ // zaten disconnect edilmiş olabilir; moveInto bağlıysa moveBefore, değilse appendChild kullanır.
704
+ this.moveInto(hiddenContainer, element);
705
+ console.log('Web component element hidden container\'a taşındı (state korunuyor)');
701
706
  }
702
707
  catch (error) {
703
708
  console.warn('Web component element preserve edilirken hata:', error);
@@ -720,35 +725,45 @@ class CustomRouteReuseStrategy {
720
725
  return;
721
726
  const element = componentInstance.element;
722
727
  const hiddenContainer = this.getHiddenContainer();
723
- // Eğer element hidden container'da ise, çıkar
724
- if (hiddenContainer.contains(element)) {
725
- hiddenContainer.removeChild(element);
726
- delete element.__arilPreserveDuringDetach;
727
- console.log('Web component element hidden container\'dan çıkarıldı');
728
- // Component view'ın native element'ine eklemeyi dene
729
- const tryRestore = () => {
730
- const vc = componentInstance.vc;
731
- if (vc?.nativeElement) {
732
- vc.nativeElement.appendChild(element);
733
- console.log('Web component element component view\'a eklendi (retrieve sırasında)');
734
- // Props'ları güncellemek için flag set et (component kendi populateProps'unu çağıracak)
735
- componentInstance.propsUpdated = false;
736
- return true;
737
- }
728
+ if (!hiddenContainer.contains(element))
729
+ return;
730
+ // ÖNEMLI: element'i hidden container'dan ERKEN çıkarmıyoruz. `retrieve`, Angular
731
+ // `outlet.attach()`'ten ÖNCE çağrılır → vc henüz DOM'a bağlı değildir. Erken removeChild
732
+ // element'i disconnect eder ve iframe reload olur (tam kaçındığımız şey). Bunun yerine
733
+ // element'i hidden container'da (bağlı) bırakıp vc bağlanınca atomic moveBefore ile
734
+ // doğrudan hidden vc taşıyoruz; iframe hiç reload olmaz.
735
+ const tryRestore = () => {
736
+ const vcNative = componentInstance.vc?.nativeElement;
737
+ if (!vcNative || !vcNative.isConnected)
738
738
  return false;
739
- };
740
- // Hemen dene
741
- if (!tryRestore()) {
742
- // View henüz hazır değilse, bir sonraki tick'te tekrar dene
743
- setTimeout(() => {
744
- if (!tryRestore()) {
745
- // Hala başarısızsa, flag set et (ngAfterViewInit'te tekrar denenecek)
746
- componentInstance.needsRestore = true;
747
- console.log('Web component element restore edilecek (view hazır değil, ngAfterViewInit\'te tekrar denenecek)');
748
- }
749
- }, 0);
739
+ this.moveInto(vcNative, element);
740
+ // Flag'i moveInto SONRASI sil: fallback removeChild yaparsa disconnectedCallback flag'i hâlâ görsün.
741
+ delete element.__arilPreserveDuringDetach;
742
+ componentInstance.propsUpdated = false;
743
+ console.log('Web component element component view\'a eklendi (retrieve sırasında)');
744
+ return true;
745
+ };
746
+ // Hemen dene (vc genelde bağlı değil) → değilse microtask'te tekrar dene. `outlet.attach()`
747
+ // retrieve'den hemen sonra SENKRON çalışır; microtask geçerli task bitince (attach tamamlanmış,
748
+ // vc bağlı) ama PAINT'ten ÖNCE koşar → element yerine oturur, tab geçişinde boş-flash olmaz.
749
+ if (tryRestore())
750
+ return;
751
+ queueMicrotask(() => {
752
+ if (tryRestore())
753
+ return;
754
+ // Son çare: vc hâlâ bağlı değil. Element'i yine de vc'ye taşı ki tab boş kalmasın
755
+ // (attach view'ı bağlayınca görünür olur — iframe reload olabilir ama eski davranış korunur).
756
+ const vcNative = componentInstance.vc?.nativeElement;
757
+ if (vcNative) {
758
+ this.moveInto(vcNative, element);
759
+ delete element.__arilPreserveDuringDetach;
760
+ componentInstance.propsUpdated = false;
750
761
  }
751
- }
762
+ else {
763
+ componentInstance.needsRestore = true;
764
+ console.log('Web component element restore edilecek (view hazır değil, ngAfterViewInit\'te tekrar denenecek)');
765
+ }
766
+ });
752
767
  }
753
768
  catch (error) {
754
769
  console.warn('Web component element restore edilirken hata:', error);
@@ -772,6 +787,61 @@ class CustomRouteReuseStrategy {
772
787
  }
773
788
  return container;
774
789
  }
790
+ /**
791
+ * `el`'i `target`'ın son çocuğu olacak şekilde TAŞIR. `moveBefore` (atomic move) destekleniyor ve
792
+ * her iki düğüm de bağlıysa onu kullanır: nested browsing context (iframe) reload OLMADAN ve custom
793
+ * element'in connected/disconnected callback'leri tetiklenmeden taşır → iframe-tabanlı embed'lerin
794
+ * (Superset) auth handshake'i + scroll/filtre state'i korunur. Desteklenmiyorsa veya taşıma kısıtları
795
+ * ihlal edilirse (disconnected / cross-document) removeChild+appendChild'a düşer (eski davranış: iframe
796
+ * reload olur ama işlem güvenli tamamlanır).
797
+ */
798
+ moveInto(target, el) {
799
+ const mover = target;
800
+ if (typeof mover.moveBefore === 'function' && el.isConnected && target.isConnected) {
801
+ try {
802
+ mover.moveBefore(el, null);
803
+ return;
804
+ }
805
+ catch {
806
+ // moveBefore kısıtları ihlal edildi → aşağıdaki klasik taşımaya düş.
807
+ }
808
+ }
809
+ if (el.parentNode && el.parentNode !== target) {
810
+ el.parentNode.removeChild(el);
811
+ }
812
+ if (!target.contains(el)) {
813
+ target.appendChild(el);
814
+ }
815
+ }
816
+ /**
817
+ * `shouldDetach` içinden, Angular `outlet.detach()` çalışmadan ÖNCE çağrılır: canlı outlet'teki MFE
818
+ * element'ini `aril-tab-id` attribute'undan bulup atomic `moveBefore` ile hidden container'a taşır.
819
+ * Böylece Angular wrapper view'ını söktüğünde `<app-xxx>` element'i zaten güvenli/bağlı konumdadır ve
820
+ * barındırdığı iframe reload olmaz. Element bulunamaz ya da zaten hidden container'daysa no-op →
821
+ * `store()` eski yoluyla devreye girer (regresyon yok).
822
+ */
823
+ preserveLiveElementBeforeDetach(route) {
824
+ try {
825
+ const tabId = this.getTabIdFromRoute(route);
826
+ if (!tabId)
827
+ return;
828
+ const hiddenContainer = this.getHiddenContainer();
829
+ const candidates = document.querySelectorAll(`[aril-tab-id="${tabId}"]`);
830
+ for (const candidate of Array.from(candidates)) {
831
+ const el = candidate;
832
+ if (hiddenContainer.contains(el))
833
+ continue;
834
+ // Fallback removeChild yolunda disconnectedCallback'in destroy'ı atlaması için flag'i taşımadan ÖNCE set et.
835
+ el.__arilPreserveDuringDetach = true;
836
+ this.moveInto(hiddenContainer, el);
837
+ console.log(`[Strategy] pre-detach preserve: <${el.tagName.toLowerCase()}> tab="${tabId}" hidden container'a taşındı`);
838
+ break;
839
+ }
840
+ }
841
+ catch (error) {
842
+ console.warn('[Strategy] pre-detach preserve hatası:', error);
843
+ }
844
+ }
775
845
  /**
776
846
  * Detached route view'ı içindeki plugin/MFE custom element'lerini (tag adında `-`
777
847
  * bulunan ve `customElements`'a kayıtlı olanları) hidden container'a taşır ve