aril 2.0.1-dev.3 → 2.0.1-dev.4
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/boot/bridge/src/mfe-bridge.d.ts +33 -0
- package/esm2022/boot/bridge/src/mfe-bridge.mjs +18 -1
- package/esm2022/boot/mfe/src/bootstrap.mjs +5 -1
- package/esm2022/theme/layout/service/breadcrumb-publisher.service.mjs +12 -4
- package/esm2022/theme/layout/service/interfaces/interfaces.mjs +1 -1
- package/esm2022/theme/layout/service/selection-group.service.mjs +151 -21
- package/fesm2022/aril-boot-bridge.mjs +17 -0
- package/fesm2022/aril-boot-bridge.mjs.map +1 -1
- package/fesm2022/{aril-boot-mfe-app.component-AS4MzEo2.mjs → aril-boot-mfe-app.component-oxzMhRXe.mjs} +2 -2
- package/fesm2022/{aril-boot-mfe-app.component-AS4MzEo2.mjs.map → aril-boot-mfe-app.component-oxzMhRXe.mjs.map} +1 -1
- package/fesm2022/{aril-boot-mfe-aril-boot-mfe-Ui9Cv3UC.mjs → aril-boot-mfe-aril-boot-mfe-CKOM61xb.mjs} +6 -2
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-CKOM61xb.mjs.map +1 -0
- package/fesm2022/aril-boot-mfe.mjs +1 -1
- package/fesm2022/aril-theme-layout.mjs +158 -21
- package/fesm2022/aril-theme-layout.mjs.map +1 -1
- package/fesm2022/aril.mjs +4 -0
- package/fesm2022/aril.mjs.map +1 -1
- package/package.json +203 -203
- package/scripts/mf-shared.js +47 -0
- package/theme/layout/service/breadcrumb-publisher.service.ts +18 -8
- package/theme/layout/service/interfaces/interfaces.d.ts +3 -17
- package/theme/layout/service/interfaces/interfaces.ts +6 -18
- package/theme/layout/service/selection-group.service.d.ts +8 -4
- package/theme/layout/service/selection-group.service.ts +161 -25
- package/fesm2022/aril-boot-mfe-aril-boot-mfe-Ui9Cv3UC.mjs.map +0 -1
|
@@ -3468,7 +3468,12 @@ class BreadcrumbPublisherService {
|
|
|
3468
3468
|
// değerleri sonradan geldikçe listeyi yeniden yayınla.
|
|
3469
3469
|
this.breadcrumbService.keyValues();
|
|
3470
3470
|
this.publishFromCurrentRoute();
|
|
3471
|
-
}
|
|
3471
|
+
},
|
|
3472
|
+
// `publishFromCurrentRoute` → `publish` içinde `bridge.breadcrumbs.set` / `bridge.pageTitle.set`
|
|
3473
|
+
// signal write yapar; effect içinden signal write default'ta NG0600 fırlatır. aril genelindeki
|
|
3474
|
+
// effect'lerle aynı şekilde `allowSignalWrites` ile izin veriyoruz. (MF shared core öncesi
|
|
3475
|
+
// cross-core split bu guard'ı maskeliyordu; tek core olunca yüzeye çıktı — davranış aynı.)
|
|
3476
|
+
{ allowSignalWrites: true });
|
|
3472
3477
|
}
|
|
3473
3478
|
ngOnDestroy() {
|
|
3474
3479
|
this.routerSubscription?.unsubscribe();
|
|
@@ -3503,7 +3508,10 @@ class BreadcrumbPublisherService {
|
|
|
3503
3508
|
publish(breadcrumbs) {
|
|
3504
3509
|
bridge.breadcrumbs.set(breadcrumbs);
|
|
3505
3510
|
const lastLabel = breadcrumbs[breadcrumbs.length - 1]?.label ?? '';
|
|
3506
|
-
|
|
3511
|
+
// untracked ZORUNLU: publish, constructor'daki effect'in çağrı grafiğinde çalışır. Tracked
|
|
3512
|
+
// okuma effect'i kendi yazdığı pageTitle'a abone eder; tab başına bir publisher instance
|
|
3513
|
+
// olduğundan iki tab farklı title yazarak birbirini sonsuz döngüde tetiklerdi.
|
|
3514
|
+
if (untracked(bridge.pageTitle) || lastLabel) {
|
|
3507
3515
|
bridge.pageTitle.set(lastLabel);
|
|
3508
3516
|
}
|
|
3509
3517
|
this.pubSubService.publish({
|
|
@@ -3562,34 +3570,148 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImpor
|
|
|
3562
3570
|
}]
|
|
3563
3571
|
}], ctorParameters: () => [] });
|
|
3564
3572
|
|
|
3573
|
+
// Key başına maksimum ardışık fetch hatası: limit aşılınca o key oturum boyu yeniden denenmez.
|
|
3574
|
+
// Sınırsız retry'da kalıcı hata veren bir grup (örn. silinmiş grup → 404), diğer grupların her
|
|
3575
|
+
// başarılı yüklemesinde (map güncellemesi tüm enum computed'larını yeniden çalıştırır) yeniden
|
|
3576
|
+
// POST edilirdi → istek/console fırtınası.
|
|
3577
|
+
const MAX_FETCH_FAILURES = 3;
|
|
3565
3578
|
class SelectionGroupService {
|
|
3566
3579
|
constructor(http) {
|
|
3567
3580
|
this.http = http;
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
this.
|
|
3571
|
-
this.
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3581
|
+
// State cross-MFE paylaşımlı: bridge globalThis-singleton'ında yaşar (bkz. mfe-bridge.ts).
|
|
3582
|
+
// Servis instance'ı MFE/tab başına çoğalsa da tüm okuma/yazma tek bridge state'ine gider.
|
|
3583
|
+
this.selectionGroups = bridge.selectionGroups;
|
|
3584
|
+
this.loading = bridge.selectionLoading;
|
|
3585
|
+
this.loaded = bridge.selectionLoaded;
|
|
3586
|
+
this.failures = bridge.selectionFailures;
|
|
3587
|
+
// loadStates + invalidate yalnızca ilk instance'ta (cross-MFE bir kez): tek localStorage
|
|
3588
|
+
// yüklemesi, tek `/versions` çağrısı. effect her instance'ta kurulur ama hepsi aynı paylaşılan
|
|
3589
|
+
// state'i yazdığından ezme olmaz.
|
|
3590
|
+
if (!bridge.selectionInitialized) {
|
|
3591
|
+
bridge.selectionInitialized = true;
|
|
3592
|
+
this.loadStatesFromLocalStorage();
|
|
3593
|
+
this.invalidateStaleGroups();
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
// localStorage'a slim cache yazar. `effect` YERİNE bilinçli olarak doğrudan çağrılır: signal'i
|
|
3597
|
+
// değiştiren her yer (fetchEnum, invalidateStaleGroups) persist'i açıkça tetikler. NOT: webpack MF
|
|
3598
|
+
// config'iyle `@angular/core` artık singleton shared olduğundan effect-tabanlı persist de çalışırdı;
|
|
3599
|
+
// yine de açık/öngörülebilir olması (effect içinde gizli signal-write + NG0600 riski olmaması) için
|
|
3600
|
+
// doğrudan çağrı tercih edildi.
|
|
3601
|
+
persistToLocalStorage() {
|
|
3602
|
+
try {
|
|
3603
|
+
const slim = this.slimForStorage(this.selectionGroups());
|
|
3604
|
+
localStorage.setItem('selection-groups', btoa(encodeURI(JSON.stringify(slim))));
|
|
3605
|
+
}
|
|
3606
|
+
catch {
|
|
3607
|
+
// QuotaExceededError vb. → eski cache'i koru, yeni yazımı sessizce atla.
|
|
3608
|
+
}
|
|
3576
3609
|
}
|
|
3577
|
-
|
|
3610
|
+
loadStatesFromLocalStorage() {
|
|
3578
3611
|
const selectionGroups = localStorage.getItem('selection-groups');
|
|
3579
3612
|
if (selectionGroups) {
|
|
3580
3613
|
try {
|
|
3581
3614
|
const decodedData = decodeURI(atob(selectionGroups));
|
|
3582
|
-
|
|
3615
|
+
// Dynamic gruplar zaten persist edilmiyor (slimForStorage onları atlar) → ekstra
|
|
3616
|
+
// filtre gerekmez; localStorage içeriği yalnızca Fixed gruplardır.
|
|
3617
|
+
const restored = JSON.parse(decodedData);
|
|
3618
|
+
this.selectionGroups.set(restored);
|
|
3619
|
+
// Restore edilen gruplar loaded sayılır: aksi halde koşulsuz fetchEnum çağıran
|
|
3620
|
+
// consumer'lar guard'ı geçip her oturum yeniden çeker (cache faydası kaybolur).
|
|
3621
|
+
// Stale'lik kontrolü invalidateStaleGroups'un işi. null entry'ler (legacy) loaded
|
|
3622
|
+
// işaretlenmez — işaretlenirse o grup bir daha hiç yüklenemezdi.
|
|
3623
|
+
Object.entries(restored).forEach(([key, group]) => {
|
|
3624
|
+
if (group)
|
|
3625
|
+
this.loaded.set(key, true);
|
|
3626
|
+
});
|
|
3583
3627
|
}
|
|
3584
3628
|
catch (e) {
|
|
3585
3629
|
console.error(e);
|
|
3586
3630
|
}
|
|
3587
3631
|
}
|
|
3588
3632
|
}
|
|
3633
|
+
// Dynamic grupları atlar + Fixed grupları yalnızca render (value/table/picker/export) ve invalidate
|
|
3634
|
+
// için gerekli alanlara indirger. localStorage'da audit/id/açıklama gibi kullanılmayan alanları tutmaz.
|
|
3635
|
+
slimForStorage(groups) {
|
|
3636
|
+
const out = {};
|
|
3637
|
+
for (const [key, group] of Object.entries(groups)) {
|
|
3638
|
+
if (!group || group.loadStrategy === 'Dynamic')
|
|
3639
|
+
continue;
|
|
3640
|
+
out[key] = {
|
|
3641
|
+
version: group.version,
|
|
3642
|
+
selectionItems: (group.selectionItems ?? []).map((it) => ({
|
|
3643
|
+
selectionKey: it.selectionKey,
|
|
3644
|
+
selectionDisplay: it.selectionDisplay,
|
|
3645
|
+
selectionMultiLanguageDisplays: it.selectionMultiLanguageDisplays,
|
|
3646
|
+
showOrder: it.showOrder,
|
|
3647
|
+
parentSelectionKey: it.parentSelectionKey
|
|
3648
|
+
}))
|
|
3649
|
+
};
|
|
3650
|
+
}
|
|
3651
|
+
return out;
|
|
3652
|
+
}
|
|
3653
|
+
// App init'te enum version'larını çekip localStorage'daki stale grupları temizler.
|
|
3654
|
+
// Silinen grup, tüketici component'lerin `if (!selectionGroup) fetchEnum()` guard'ıyla yeniden yüklenir.
|
|
3655
|
+
invalidateStaleGroups() {
|
|
3656
|
+
// App-init snapshot: yalnızca localStorage'dan yüklenen gruplar kontrol edilir. Yanıt gelene
|
|
3657
|
+
// kadar consumer'ların fetchEnum ile yüklediği taze gruplar taranmaz/silinmez (aksi halde
|
|
3658
|
+
// taze grup silinir ve kalıcı per-component fetch guard'ları onu bir daha çekmeyebilir).
|
|
3659
|
+
const keysToCheck = Object.keys(this.selectionGroups());
|
|
3660
|
+
if (keysToCheck.length === 0)
|
|
3661
|
+
return;
|
|
3662
|
+
const hostApi = API_CONFIGS.api.replace(/\/[^\/]+\/v1$/, `/host/v1`);
|
|
3663
|
+
this.http
|
|
3664
|
+
.get(hostApi + '/selection-group/versions')
|
|
3665
|
+
.pipe(take(1))
|
|
3666
|
+
.subscribe({
|
|
3667
|
+
next: (versions) => {
|
|
3668
|
+
// Boş/bozuk yanıt güvenlik şeridi: aşağıdaki "listede olmayanı sil" kuralı
|
|
3669
|
+
// boş listeyle tüm cache'i yanlışlıkla boşaltmasın.
|
|
3670
|
+
if (!versions?.length)
|
|
3671
|
+
return;
|
|
3672
|
+
const latest = new Map(versions.map((v) => [v.groupName, v.version]));
|
|
3673
|
+
let changed = false;
|
|
3674
|
+
const next = { ...this.selectionGroups() };
|
|
3675
|
+
keysToCheck.forEach((key) => {
|
|
3676
|
+
const group = next[key];
|
|
3677
|
+
if (!group)
|
|
3678
|
+
return;
|
|
3679
|
+
const separatorIndex = key.indexOf('~');
|
|
3680
|
+
if (separatorIndex === -1)
|
|
3681
|
+
return;
|
|
3682
|
+
const serverVersion = latest.get(key.substring(0, separatorIndex));
|
|
3683
|
+
// serverVersion == null: grup /versions listesinde yok → sunucudan silinmiş /
|
|
3684
|
+
// yeniden adlandırılmış demektir (endpoint Fixed grupların tam listesini döner)
|
|
3685
|
+
// → o da evict edilir; kalırsa kaldırılmış enum değerleri süresiz render edilir.
|
|
3686
|
+
if (serverVersion == null || serverVersion !== group.version) {
|
|
3687
|
+
delete next[key];
|
|
3688
|
+
this.loaded.delete(key);
|
|
3689
|
+
this.failures.delete(key);
|
|
3690
|
+
// Uçuştaki fetch'in flag'i korunur: silinirse ikinci bir consumer guard'ı
|
|
3691
|
+
// geçip duplicate /load atar ve export-overlay'in isLoading poll'ü grubu
|
|
3692
|
+
// beklemeden erken geçer. Yanıt geldiğinde flag'i fetchEnum kendisi yönetir.
|
|
3693
|
+
if (!this.loading.get(key)) {
|
|
3694
|
+
this.loading.delete(key);
|
|
3695
|
+
}
|
|
3696
|
+
changed = true;
|
|
3697
|
+
}
|
|
3698
|
+
});
|
|
3699
|
+
if (changed) {
|
|
3700
|
+
this.selectionGroups.set(next);
|
|
3701
|
+
this.persistToLocalStorage();
|
|
3702
|
+
}
|
|
3703
|
+
},
|
|
3704
|
+
error: () => {
|
|
3705
|
+
// versions endpoint yok/erişilemez: sessizce mevcut cache ile devam.
|
|
3706
|
+
}
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3589
3709
|
fetchEnum(groupName, parentSelectionKey = 'ALL') {
|
|
3590
3710
|
const key = groupName + '~' + parentSelectionKey;
|
|
3591
3711
|
if (this.loaded.get(key) || this.loading.get(key))
|
|
3592
3712
|
return;
|
|
3713
|
+
if ((this.failures.get(key) ?? 0) >= MAX_FETCH_FAILURES)
|
|
3714
|
+
return;
|
|
3593
3715
|
const payload = {
|
|
3594
3716
|
groupName,
|
|
3595
3717
|
parentSelectionKey
|
|
@@ -3600,17 +3722,32 @@ class SelectionGroupService {
|
|
|
3600
3722
|
this.http
|
|
3601
3723
|
.post(hostApi + '/selection-group/load', payload)
|
|
3602
3724
|
.pipe(take(1))
|
|
3603
|
-
.subscribe(
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3725
|
+
.subscribe({
|
|
3726
|
+
next: (selectionGroup) => {
|
|
3727
|
+
this.loading.set(key, false);
|
|
3728
|
+
this.loaded.set(key, true);
|
|
3729
|
+
this.failures.delete(key);
|
|
3730
|
+
this.selectionGroups.update((groups) => ({
|
|
3731
|
+
...groups,
|
|
3732
|
+
[key]: selectionGroup
|
|
3733
|
+
}));
|
|
3734
|
+
this.persistToLocalStorage();
|
|
3735
|
+
},
|
|
3736
|
+
// HTTP hatasında loading'i sıfırla: aksi halde guard (loaded/loading) bu enum'u
|
|
3737
|
+
// kilitler. loading/loaded bridge'de cross-MFE SHARED olduğundan, tek bir anlık hata
|
|
3738
|
+
// bu enum'u TÜM remote'larda hard-reload'a kadar bir daha çekilemez yapardı. loading=false
|
|
3739
|
+
// → sonraki fetchEnum (component init/retry) yeniden deneyebilir. loaded zaten false kalır.
|
|
3740
|
+
// Deneme sayısı MAX_FETCH_FAILURES ile sınırlı: kalıcı hatada sonsuz retry önlenir.
|
|
3741
|
+
error: () => {
|
|
3742
|
+
this.loading.set(key, false);
|
|
3743
|
+
this.failures.set(key, (this.failures.get(key) ?? 0) + 1);
|
|
3744
|
+
}
|
|
3610
3745
|
});
|
|
3611
3746
|
}
|
|
3612
|
-
|
|
3613
|
-
|
|
3747
|
+
// key formatı: `groupName~parentSelectionKey` (fetchEnum'daki composite key ile aynı).
|
|
3748
|
+
// Direkt olarak groupName geçilirse Map'te eşleşme olmaz ve her zaman false döner.
|
|
3749
|
+
isLoading(key) {
|
|
3750
|
+
return this.loading.get(key) ?? false;
|
|
3614
3751
|
}
|
|
3615
3752
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: SelectionGroupService, deps: [{ token: i6.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3616
3753
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: SelectionGroupService, providedIn: 'root' }); }
|