--- hüküm: tamamlandı tip: dokümantasyon okur: FW-CSS-MOTOR-1 / FW-CSS-MOTOR-2, proje geliştiricileri, ajanlar platform: Windows --- # 05 — Asset Sistemi (CSS / JS / Font / İkon Yükleme) > **Bu belge hangi commit'e göre yazıldı:** `d4af18d` (dal `feat/fw-license-master`, 2026-10-05 20:49) > **Son doğrulama tarihi:** 2026-10-05 > **Yayın tabanı:** 0.9.7 = bu commit + sonrası; belge yalnız doğrulama anındaki kodu anlatır > **Kapsam:** `rbnframework/` deposunun asset katmanı — `Core/Support/Definitions/Render/*`, > `Core/Render/{Builders,Controllers,Services,Providers,Configs}/*`, `Resources/Assets/RbnCommon/*` > **Yazım kuralı:** Her iddia `dosya:satır` ile kaynağa bağlıdır. Ölçülen sayılar **bu belgedeki > ölçüm komutunun çıktısıdır**; başka bir yerden alınmış tahmin yoktur. Kodla doğrulanamayanlar > §8 `BILINMEYENLER` bölümündedir. > **Bu belge KOD DEĞİŞTİRMEZ.** Yalnız okur/uygular. > **ÖNCEKİ YANLIŞ VARSAYIMIN DÜZELTİLMESİ** > *"framework-assets/fonts/space-mono yerelden gelir"* → **YANLIŞ**. > `AssetController::serve()` `fonts/` yolunu `AssetFonts::FONT_LIBRARY` içindeki **Google Fonts > URL'sine 307 ile yönlendirir**. Yerel font **dosyası sunulmaz**. > Canlı kanıt (§2.4, `curl` çıktısı): > ``` > GET /framework-assets/fonts/space-mono → 307 > Location: https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap > ``` > Bu bir **tasarım kararıdır, hata değil**: §2.5'te anlatıldığı gibi Google Fonts / Google Tag Manager / > Remix Icon **kalıcıdır**; `AssetDefinition` CDN adresleri **bilinçli çok yönlüdür** (isteyen kullanır). --- ## İçindekiler 1. [Yükleme sistemi uçtan uca](#1-yükleme-sistemi-uçtan-uca) 2. [Sabit tablosu (`AssetDefinition`)](#2-sabit-tablosu-assetdefinition) 3. [`AssetBundles` — paketler ve bağlam yığınları](#3-assetbundles--paketler-ve-bağlam-yığınları) 4. [`AssetBuilder` — sıralama, `css_engine` kararı, URL üretimi, sürümleme](#4-assetbuilder--sıralama-css_engine-kararı-url-üretimi-sürümleme) 5. [`AssetController` — sunum uçları, font yönlendirmesi, medya vekili](#5-assetcontroller--sunum-uçları-font-yönlendirmesi-medya-vekili) 6. [Font sistemi (`AssetFonts`)](#6-font-sistemi-assetfonts) 7. [CSS katmanları](#7-css-katmanları) 8. [JS katmanları](#8-js-katmanları) 9. [İkon sistemi](#9-ikon-sistemi) 10. [Favicon / OG görseli konvansiyonu (`AssetConvention`)](#10-favicon--og-görseli-konvansiyonu-assetconvention) 11. ["Bir kaynağı nasıl ister / değiştiririm" tarifleri](#11-bir-kaynağı-nasıl-ister--değiştiririm-tarifleri) 12. [Performans notları (yalnız ölçülen)](#12-performans-notları-yalnız-ölçülen) 13. [BILINMEYENLER](#13-bilinmeyenler) --- ## 1. Yükleme sistemi uçtan uca Altı katman, tek yönde çalışır: ``` (1) project-routemap.php → css_engine: 'rbn' | belirtilmemiş (= bootstrap) (2) AssetDefinition.php → tek dosya yol/CDN sabitleri (saf kayıt defteri, mantık YOK) (3) AssetBundles.php → BUNDLES (hangi asset hangi paket) + STACK_MAP (hangi paket hangi bağlamda) (4) AssetBuilder.php → injectCoreStack() → collect() → compile() → build() [sıralama + URL] (5) AssetController.php → /framework-assets/… , /project-assets/… [fiziksel sunum] (6) Controller addAsset() → yalnızca SAYFAYA ÖZEL ek varlık ``` Yol haritası: | Katman | Dosya | Satır sayısı | |---|---|---| | (2) | `Core/Support/Definitions/Render/AssetDefinition.php` | 64 | | (3) | `Core/Support/Definitions/Render/AssetBundles.php` | 82 | | (6) | `Core/Render/Controllers/FrontendBaseController.php` | 222 | | (4) | `Core/Render/Builders/AssetBuilder.php` | 317 | | (5) | `Core/Render/Controllers/AssetController.php` | 402 | | keşif | `Core/System/Discovery/Clusters/Resources/AssetResolver.php` | 123 | | boyama | `Core/Render/Providers/AssetProvider.php` | 112 | | orkestrasyon | `Core/Render/Services/AssetService.php` | 96 | | rota | `Core/Routes/Mappings/core.php:46-48` | 71 (dosya) | Rotalar (`Core/Routes/Mappings/core.php:45-48`): ```php // Framework Assets Proxy Route::prefix('framework-assets')->get('{path:.+}', 'AssetController@serve'); // Project Assets Proxy (Dynamic) Route::prefix('project-assets')->get('{path:.+}', 'AssetController@serveProject'); ``` `serveProject()` (`AssetController.php:315-318`) yalnızca `serve($path, true)` çağırır; tek fark `$isProject = true` — sanal SVG ve sanal OG görseli dalları sadece bu bayrakla açılır (`AssetController.php:107`, `:124`). **Token sözleşmesi** (`AssetConfig::PROXY_SETUP`, `Core/Render/Configs/AssetConfig.php:24-34`): | Önek | Proxy yolu | Kaynak | |---|---|---| | `@project/` | `/project-assets/` | projenin `public/` kökü | | `@fw/` | `/framework-assets/` | framework `Resources/Assets/` | | `@font/` | `/framework-assets/fonts/` | **yok — 307 yönlendirme** (§6) | Token ayrıştırma `AssetBuilder::resolveToken()` (`:350-369`), sırayla `project` → `framework` → `font` denenir; ilk eşleşen önek kazanır. --- ## 2. Sabit tablosu (`AssetDefinition`) `Core/Support/Definitions/Render/AssetDefinition.php` — **36 `public const`**. Sınıf dokümanı (`:7-12`) bunu açıkça söyler: *"This file is a PURE REGISTRY … Hiçbir mantık yok."* Doğrulandı: dosyada tek bir `if`/`foreach`/metot yok. ### 2.1 Dış CDN sabitleri (tür: **CDN**) | Sabit | Satır | URL / Yol | Hangi bundle | |---|---|---|---| | `BOOTSTRAP_CSS` | 18 | `https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css` | `bootstrap` | | `BOOTSTRAP_JS` | 19 | `https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js` | `bootstrap` | | `FONT_AWESOME` | 21 | `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css` | `font_awesome` | | `BOOTSTRAP_ICONS` | 22 | `https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css` | `bootstrap_icons` | | `REMIX_ICON` | 23 | `https://cdn.jsdelivr.net/npm/remixicon@4.2.0/fonts/remixicon.css` | `remix_icon` | | `FLAG_ICON` | 24 | `https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/6.6.6/css/flag-icons.min.css` | `flag_icon` | | `JQUERY` | 26 | `['path' => 'https://code.jquery.com/jquery-3.7.1.min.js', 'renderInHead' => true]` | `jquery` | | `SORTABLE_JS` | 27 | `https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js` | `sortable` | | `AOS_CSS` | 28 | `https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css` | `aos` | | `AOS_JS` | 29 | `https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js` | `aos` | > `JQUERY` **tek CDN sabiti değil**, dizi biçimindedir (`path` + `renderInHead`), çünkü ``'de > çalışması gerekir. `AssetBuilder::compile()` diziyi `is_array($item)` dalında açıp `renderInHead` > ve `attributes` alanlarını korur (`:311-312`). ### 2.2 Framework ortak varlıkları (tür: **yerel**, token `@fw/`) | Sabit | Satır | Yol | Hangi bundle | |---|---|---|---| | `RBN_ADMIN_CSS` | 34 | `@fw/RbnAdmin/css/rbnAdmin.css` | `rbn_core_panel` | | `RBN_ADMIN_JS` | 35 | `@fw/RbnAdmin/js/rbnAdmin.js` | `rbn_core_panel` | | `RBN_ADMIN_VARIABLES_CSS` | 36 | `@fw/RbnAdmin/css/variables.css` | `admin_core_css` | | `RBN_ADMIN_GLOBAL_CSS` | 37 | `@fw/RbnAdmin/css/global.css` | `admin_core_css` | | `RBN_ADMIN_APP` | 38 | `@fw/RbnAdmin/js/rbnAdminApp.js` | `security` | | `RBN_MASTER_JS` | 44 | `@fw/RbnCommon/js/rbn-master.js` | `rbn_master_js` **ve** `rbn_master` | | `RBN_COMMON_CSS` | 45 | `@fw/RbnCommon/css/rbn-common.css` | `rbn_core_frontend` | | `RBN_MASTER_CSS` | 46 | `@fw/RbnCommon/css/rbn-master.css` | `rbn_core_panel`, `rbn_core_auth`, `rbn_master` | | `RBN_SHIELD_CSS` | 47 | `@fw/RbnCommon/css/rbn-shield.css` | **HİÇBİR PAKETTE YOK** (§13-B1) | | `RBN_DASHBOARD_CSS` | 48 | `@fw/RbnCommon/css/core/dashboard.css` | `rbn_core_panel`, `rbnDashboard` | | `RBN_AUTH_CSS` | 49 | `@fw/RbnCommon/css/core/rbn-auth.css` | `rbn_core_auth` | | `RBN_EXTENDED_UTILITIES_CSS` | 55 | `@fw/RbnCommon/css/optional/rbn-utilities-extended.css` | `rbnExtended` | | `RBN_EXTENDED_COMPONENTS_CSS` | 56 | `@fw/RbnCommon/css/optional/rbn-components-extended.css` | `rbnExtended` | | `RBN_SERVICE` | 61 | `@fw/RbnCommon/js/Networking/rbnService.js` | **HİÇBİR PAKETTE YOK** (§13-B1) | | `RBN_DEBUG_BRIDGE` | 62 | `@fw/RbnCommon/js/Networking/DebugBridge.js` | **HİÇBİR PAKETTE YOK** (§13-B1) | | `RBN_ALERT_JS` | 63 | `@fw/RbnCommon/js/components/rbnAlert.js` | **HİÇBİR PAKETTE YOK** — `rbn-master.js` dinamik yüklüyor (§8.1) | | `RBN_ALERT_CSS` | 64 | `@fw/RbnCommon/css/components/rbnAlert.css` | `rbn_core_frontend` | | `RBN_MODAL_JS` | 65 | `@fw/RbnCommon/js/components/rbnModal.js` | **HİÇBİR PAKETTE YOK** — dinamik yükleniyor | | `RBN_MODAL_CSS` | 66 | `@fw/RbnCommon/css/components/rbnModal.css` | `rbnModal` | | `RBN_UTILS` | 67 | `@fw/RbnCommon/js/Utils/rbnUtils.js` | **HİÇBİR PAKETTE YOK** — dinamik yükleniyor | | `RBN_BINDERS` | 68 | `@fw/RbnCommon/js/Utils/rbnBinders.js` | **HİÇBİR PAKETTE YOK** — dinamik yükleniyor | | `RBN_AI` | 69 | `@fw/RbnCommon/js/Utils/rbnAi.js` | **HİÇBİR PAKETTE YOK** | | `RBN_CHARTS_JS` | 70 | `@fw/RbnCommon/js/components/rbnCharts.js` | `rbnCharts` | | `RBN_CHARTS_CSS` | 71 | `@fw/RbnCommon/css/components/rbnCharts.css` | `rbnCharts` | | `RBN_TABLE_JS` | 72 | `@fw/RbnCommon/js/components/rbnTable.js` | `rbnTable` | | `RBN_TABLE_CSS` | 73 | `@fw/RbnCommon/css/components/rbnTable.css` | `rbnTable` | **Önemli gözlem (davranış, hata değil):** `rbn-master.js` çekirdek JS modüllerini **kendisi dinamik olarak** yükler (§8.1). Bu yüzden `RBN_ALERT_JS`, `RBN_MODAL_JS`, `RBN_UTILS`, `RBN_BINDERS` sabitleri vardır ama **hiçbir pakete bağlı değildir** — tanımları `BUNDLES` yerine `rbn-master.js` içindeki `coreModules` dizisini besler. Yeni bir çekirdek JS modülü eklemek için iki yer güncellenir: sabit + `rbn-master.js` dizisi. ### 2.3 Token'ı doğrudan yazan paket girdileri `AssetBundles` içinde sabit kullanılmayan iki yer: | Değer | Satır | Ne yapar | |---|---|---| | `'@project/css/master.css'` | `AssetBundles.php:48` (`rbn_core_frontend`) | frontend'in **proje** giriş CSS'i; her frontend sayfasında otomatik | | `'@project/master.css'` | `AssetBundles.php:90` (`project`) | ⚠️ `project` paketi `rbn_core_frontend` ile **çakışır ve kullanılmaz**; ayrıca `domains/**/css/master.css` (21 dosya) yapısıyla uyuşmaz (`@project/master.css` proje kökü bekler). §13-B2 | | `'@font/'` | `AssetBundles.php:36-37` (`fonts_panel`, `fonts_auth`) | §6 | ### 2.4 Yönlendirme (tür: **yönlendirme**) — `fonts/` `AssetController::serve()` (`AssetController.php:53-77`): 1. Yol `fonts/` ile başlıyorsa slug çıkarılır (`fonts/` soyulur). 2. `AssetFonts::FONT_LIBRARY` içindeki **her** anahtar için `strtolower(str_replace(' ', '-', $name))` slug'ı hesaplanır ve istenen slug ile karşılaştırılır. 3. Eşleşirse: hedef `FONT_LIBRARY`'den gelir (istekten **gelmez** → açık yönlendirme riski yok). Yine de iki kontrol uygulanır (`:68-72`): kontrol karakteri/CRLF yok, şema `http`/`https`. Başarısızsa `abort(404, '… guvensiz font hedefi reddedildi')`. 4. `header("Location: $url", true, 307); exit;` **Canlı ölçüm (yerel canlı, 2026-10-05):** ``` $ curl -s -k -D - https://example.tr.test/framework-assets/fonts/space-mono HTTP/1.1 307 Temporary Redirect Location: https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap $ curl -s -k -I https://example.tr.test/framework-assets/fonts/space-mono # HEAD HTTP/1.1 307 Temporary Redirect Location: https://fonts.googleapis.com/css2?family=Space+Mono:... $ curl … /framework-assets/fonts/space-mono=v1.0 # sürüm son eki HTTP/1.1 307 Temporary Redirect → aynı hedef (sürüm son eki fonts/ yolunu BOZMAZ) ``` **Yerel font dosyası `fonts/` rotasından sunulmaz.** Doğrulandı: `GET /framework-assets/fonts/BebasNeue-Regular.ttf` → **HTTP 500** (aşağıda "404 gerçekte 500" notu). Kütüphanede slug'ı olmayan bir istek de aynı şekilde 500'e düşer. > ⚠️ **Ölçülen davranış — `abort(404)` HTTP 500 döner.** > `AssetController` "bulunamadı" için `$this->abort(404, …)` çağırıyor (`AssetController.php:135`), > ama `abort()` → `shield()->abort()` → `throw new \Exception($message, $code)` > (`Core/Services/Exception/Concerns/ErrorHandlingTrait.php:65-68`, > `Core/Services/Exception/Concerns/Shield.php:86-89`) ve istisna işleyicisi 500 üretiyor. > Ölçüm: `/framework-assets/rbncommon/yok.css` → **500**; normal bir sayfa 404'ü `/yok-boyle-bir-sayfa-xyz` > → **404**. Yani **asset 404'leri şu an 500 olarak çıkıyor.** §13-B3. ### 2.5 "Bu CDN'ler neden duruyor?" — Proje sahibi kararı (2026-10-05) `AssetDefinition`'daki CDN sabitleri **bilinçli çok yönlü** tasarımdır; framework geneldir ve tek bir CSS/JS ailesi dayatmaz (Anayasa §9). Ölçülen gerçek kullanım: | Aile | Gerekçe (ÖLÇÜLDÜ) | |---|---| | **Google Fonts** | `AssetFonts::FONT_LIBRARY`'ın **33/33** girdisi `fonts.googleapis.com` (§6). Yerel font yolu yok. | | **Remix Icon** | `class="… ri-…"` içeren view satırı: **2 527** (framework + projeler). `bi-` 726, `fa*/fas-` **0**. `ri-` fiili standarttır. | | **Google Tag Manager** | `.test` ana sayfasında canlı: `https://www.googletagmanager.com/gtag/js?id=G-J49KXF3T2L`. Proje ayarı/Controller üzerinden yükleniyor (`addAsset` değil, view/`$google_analytics_code` kanalı — §13-B4). | | Bootstrap / Bootstrap Icons / Font Awesome | Yalnız `css_engine` belirtilmemiş projelerde yüklenir (§4.2). Canlıda `` ve `.test` hâlâ Bootstrap alıyor. | | `flag_icon` | Tek proje isteği: `/…/FrontendController.php:23` + framework `PanelMap.php` (`'assets' => ['rbnCharts','flag_icon']`). | | jQuery / Sortable | Yalnız `panel` bağlamında otomatik (`STACK_MAP['panel']`). Frontend'de **yok**. | **Sonuç:** Google Fonts, Google Tag Manager ve Remix Icon **KALIR**. Kaldırmak `2 527` view satırını kırar; motor `bi-` (Bootstrap Icons) karşılığı yoktur (`bi-` kullanımı 726 satır, `rbn` motorunda bu ikonlar **tarayıcıda boş** — §13-B5). --- ## 3. `AssetBundles` — paketler ve bağlam yığınları `Core/Support/Definitions/Render/AssetBundles.php`. ### 3.1 `STACK_MAP` (`:21-26`) — hangi bağlamda hangi paketler otomatik | Bağlam | Otomatik paketler (satır) | |---|---| | `universal` | `bootstrap`, `font_awesome`, `bootstrap_icons`, `remix_icon`, `rbn_master_js` (`:22`) | | `frontend` | `rbn_core_frontend` (`:23`) | | `panel` | `fonts_panel`, `jquery`, `sortable`, `rbn_core_panel`, `rbnTable` (`:24`) | | `auth` | `fonts_auth`, `rbn_core_auth` (`:25`) | > `STACK_MAP` bir **bağlam → paket adı** eşlemesidir. `universal` **her** bağlamda önce çalışır > (`AssetBuilder::injectCoreStack()`, `:257-263`), sonra bağlama özgü yığın (`:270-274`). > Yani bir `frontend` sayfası `universal` + `frontend` paketlerini alır. ### 3.2 `BUNDLES` (`:32-92`) — 24 paket | Paket | Satır | `styles` | `scripts` | |---|---|---|---| | `rbn_master_js` | 34 | — | `RBN_MASTER_JS` | | `fonts_panel` | 36 | `@font/Plus Jakarta Sans`, `@font/DM Serif Display`, `@font/JetBrains Mono` | — | | `fonts_auth` | 37 | `@font/Plus Jakarta Sans` | — | | `bootstrap` | 38 | `BOOTSTRAP_CSS` | `BOOTSTRAP_JS` | | `font_awesome` | 39 | `FONT_AWESOME` | — | | `bootstrap_icons` | 40 | `BOOTSTRAP_ICONS` | — | | `remix_icon` | 41 | `REMIX_ICON` | — | | `flag_icon` | 42 | `FLAG_ICON` | — | | `jquery` | 43 | — | `JQUERY` | | `admin_core_css` | 46 | `RBN_ADMIN_VARIABLES_CSS`, `RBN_ADMIN_GLOBAL_CSS` | — | | `rbn_core_frontend` | 47-49 | `RBN_ALERT_CSS`, `RBN_COMMON_CSS`, `@project/css/master.css` | — | | `rbn_core_panel` | 51-56 | `RBN_MASTER_CSS`, `RBN_DASHBOARD_CSS`, `RBN_ADMIN_CSS` | `RBN_ADMIN_JS` | | `rbn_core_auth` | 57-65 | `RBN_MASTER_CSS`, `RBN_AUTH_CSS` | — | | `rbnExtended` | 72-74 | `RBN_EXTENDED_UTILITIES_CSS`, `RBN_EXTENDED_COMPONENTS_CSS` | — | | `rbnModal` | 77 | `RBN_MODAL_CSS` | `RBN_MODAL_JS` | | `rbnDashboard` | 78 | `RBN_DASHBOARD_CSS` | — | | `rbnCharts` | 79 | `RBN_CHARTS_CSS` | `RBN_CHARTS_JS` | | `rbnTable` | 80 | `RBN_TABLE_CSS` | `RBN_TABLE_JS` | | `sortable` | 81 | — | `SORTABLE_JS` | | `aos` | 82 | `AOS_CSS` | `AOS_JS` | | `rbn_master` | 83-86 | `RBN_MASTER_CSS` | `RBN_MASTER_JS` | | `security` | 87 | — | `RBN_ADMIN_APP` | | `project` | 90 | `@project/master.css` | — | | `frontend` | 91 | — | — | **Ölçülen gerçek kullanım** (tüm repo, `Storage/framework` derleme önbelleği hariç): | Paket | Kullanan yer | |---|---| | `rbnTable` | 20 (framework `PanelMap`, `` `Map`/`Map` `'assets' => ['rbnTable']`) + `STACK_MAP['panel']` | | `rbnCharts` | `/…/FrontendController.php:28`, `RbnAdmin/Models/PanelMap.php` | | `aos` | `` `FrontendController.php:90`, `FrontendController.php:51`, `` `FrontendController.php` | | `flag_icon` | `/…/FrontendController.php:23`, `RbnAdmin/Models/PanelMap.php` | | `rbnDashboard` | `example/…/EmailFrontendController.php` | | `rbnModal` | **hiçbir yerden istenmiyor** — motor `rbn-master.js` yüklüyor (`:8.1`) | | `admin_core_css` | **hiçbir yerden istenmiyor** (§13-B6) | | `rbnExtended` | **hiçbir yerden istenmiyor** (§7.5) | | `security` | 22 eşleşme ama hepsi `Security`/`Syshub` **controller sınıf adı**; paket adı olarak istenmiyor (§13-B7) | ### 3.3 Paket başvurusu iki yoldan yapılır 1. **Controller:** `$this->addAsset('rbnCharts')` → `FrontendBaseController::addAsset()` (`:92-132`), `:119` pakette tanımlı olduğu için başlık dönüştürülmez; `AssetService::prepare($items, 'frontend')` çağrılır (`:128`). 2. **Paket haritası (`PanelMap` / `*Map.php`):** `'assets' => ['rbnCharts','flag_icon']` → `Core/Render/Providers/UI/PanelProvider.php:48-55`: `prepare($subModuleData['assets'], 'panel')`, sonra `headerAssets`/`footerAssets` yeniden basılır. > `addAsset()`'ın ön-eğri kuralı (`:118-122`): paket adı değilse **ve** `@`/`http`/`/` ile başlamıyorsa > başına `@project/` konur. Bu yüzden `addAsset('js/frontend.js')` ve `addAsset('/js/frontend.js')` **farklı** > yollara gider: ilki `@project/js/frontend.js`, ikinci site-kökünden çözülür. Ölçüm: `` ana sayfasında > `.../project-assets/js/frontend.js=v1789207191` (tek `