Yenilikler

Starter kit'e yeni eklenen özellikler ve iyileştirmeler burada listelenir.

2026-10-01 — v13.8.2

Değişti

  • Wayfinder artık kullanılmayan @/actions helper'larını üretmiyor. Bunlar adı yalnızca büyük/küçük harfle ayrışan iki klasör oluşturuyor ve harf duyarsız sistemlerde vue-tsc'yi kırıyordu. @/routes altındaki route helper'ları aynı; yeniden üretmek için php artisan wayfinder:generate --skip-actions.
  • api-dock attribute'ları Lvntr\ApiDock namespace'ini kullanıyor (api-dock 0.0.8). Eski yazımla yapılmış import'lar çalışmaya devam ediyor.

2026-09-25 — v13.8.1

Eklendi

  • Zengin metin editörüne buton eklenebiliyor. Link açık olan editörlerde yeni araç çubuğu düğmesi, seçili metni ya da yazdığınız etiketi bir adrese giden butona çevirir; üç stil var (ana, ikincil, çerçeveli) ve renk editör paletinden seçilebiliyor. İçerik düz HTML olarak kalır: yönetim ekranlarında buton olarak görünür, mobil uygulama da kendi native butonu olarak çizebilir; butonları tanımayan bir uygulama ise çalışan bir link gösterir. Buton stilleri için php artisan sk:update çalıştırın.

Düzeltildi

  • sk:install ve sk:update artık kurulu olan sürümü gösteriyor. Başlıkta, hangi sürüm kurulu olursa olsun hep v13.7.x yazıyordu.

2026-09-23 — v13.8.0

Eklendi

  • Ayarlar → Depolama ekranında artık Hetzner Object Storage ve Amazon S3 seçilebiliyor. Yerel disk ve DigitalOcean Spaces'in yanında depolama sürücüsü seçicisi artık Hetzner (Falkenstein / Nürnberg / Helsinki bölge seçimiyle) ve Amazon S3 seçeneklerini de sunuyor. Mevcut kurulumlarda yeni hetzner diski php artisan sk:update ile config/filesystems.php dosyasına eklenir. Hetzner gizli anahtarı, diğer depolama anahtarları gibi şifreli saklanır. Uç nokta ve URL için artık https:// yazmanız gerekmiyor; bölge seçince uç nokta, bucket adı girince URL otomatik doluyor, bölge listelerinde arama yapılabiliyor.

  • Zengin metin editörü artık içeriğin HTML'ini gösterip düzenletiyor, biçimlendirme araçları da arttı. </> düğmesi editörü HTML kaynağına geçirir; yazılanlar anında forma işlenir, kaynak açıkken kaydedilse de kaybolmaz, editörün saklayamadığı etiketler de yazarken kutunun altında gösterilir; tam ekran düğmesi içeriği tüm ekrana yayar (Esc ile çıkılır). Araç çubuğuna altı çizili, vurgu rengi ve biçimi temizle eklendi; standard preset'e görev listesi ve iki yana yaslama, full preset'e H4, satır içi kod, alt/üst simge, yazı tipi / boyut / satır aralığı seçicileri ve YouTube videosu geldi. Yeni editör paketleri için php artisan sk:update ve ardından npm install çalıştırın.

Güvenlik

  • Kaydedilen editör içeriğinde yalnızca YouTube video gömmeleri kalıyor. Diğer tüm <iframe>'ler kayıtta silinmeye devam ediyor; yeni yazı stilleri de yalnızca düz değerleri kabul ediyor.
  • config/settings.php dosyanız kit'ten eski olsa bile gizli ayarlar şifreli kalıyor. Yeni eklenen bir gizli ayar, uygulamanın yayınlanmış settings config'inde henüz listelenmiyorsa şifrelenmeden kaydediliyordu; kit'in yerleşik listesi artık her zaman uygulanıyor.

2026-09-22 — v13.7.6

Değiştirildi

  • Log görüntüleyici artık en yeni kayıtları en üstte gösteriyor. Bir günün log dosyası açıldığında ilk sayfa günün en eski kayıtlarıyla geliyordu; aradığınız son hataya ulaşmak için defalarca "daha fazla yükle" demek gerekiyordu. Artık liste en yeniden eskiye geliyor, daha fazla yükle bir sonraki (daha eski) sayfayı listenin altına ekliyor. Çok satırlı hata izleri ve Laravel formatı dışındaki satırlar eskisi gibi görünmeye devam ediyor.

2026-09-16 — v13.7.5

Eklendi

  • Tablolar artık arama satırı ile sütun başlıkları arasında bir mesaj şeridi gösterebiliyor — "bu liste salt okunur" ya da "içe aktarma başarısız" gibi durumlar için tam genişlikte bir uyarı. Ton seçilebiliyor (info, success, warn, danger, …), isteğe bağlı ikon ve kapatma butonu eklenebiliyor. Sayfa açıkken değişen bir mesaj için #message slot'u devreye giriyor.
  • Tablo toolbar'ının #toolbar-end slot'u artık çalışıyor. İçine koyduğunuz şey önceden kayboluyordu; şimdi sağda, filtre ve sütun butonlarının ardından görünüyor.

Değiştirildi

  • Satır içi filtre etiketleri artık arama kutusunun hemen yanında değil, sağdaki filtre ve sütun butonlarının yanında duruyor — filtre eklendikçe arama kutusu itilmiyor ve arama satırının çevresindeki boşluk dengeli.

2026-09-14 — v13.7.4

Düzeltildi

  • Toplu rol silme, tekil silmenin zaten reddettiği eşit-rütbeli bir rolü silebiliyordu — çünkü rütbe kontrolünü biraz daha gevşek bir karşılaştırmayla kendi içinde yeniden uyguluyordu. İki yol artık tek bir rütbe kontrolünü paylaşıyor. Scaffold tarafı (stubs/): mevcut bir kuruluma yalnızca sk:update ile ulaşır — BulkDeleteRoleAction.php'ı özelleştirmiş bir uygulamanın düzeltmeyi elle uygulaması gerekir.
  • Kayıt akışı artık login formunun zaten uyguladığı hız sınırlaması ve Turnstile CAPTCHA'sından geçiyor — bu da otomatik bir istemcinin CAPTCHA'yı çözmeden hesap açmasına izin veren açığı kapatıyor. Scaffold tarafı: mevcut kuruluma yalnızca sk:update ile ulaşır — özelleştirilmiş bir FortifyServiceProvider.php'ın bu değişikliği elle alması gerekir.
  • Parola sıfırlama artık hesabı her yerde oturumdan çıkarıyor — sıfırlamayı isteyen oturum dışında, tüm API token'ları ve tüm diğer aktif oturumlar iptal ediliyor. Giriş yapılmışken uygulama içinden parola değiştirme veya bir yöneticinin parolanızı belirlemesi henüz kapsanmıyor; bu yollar eski oturumları hâlâ canlı bırakıyor.
  • Dosya yöneticisinin çöp kutusu, başka bir kullanıcının silinmiş dosyası için çalışan bir önizleme ve gerçek bir indirme linki döndürebiliyordu, ve yalnızca oluşturma yetkisi olan bir yükleyici kendine ait olmayan bir dosyayı geri yükleyebiliyordu. İkisi de artık engellendi.
  • Yazma yetkiniz olmayan bir klasöre dosya yüklemek artık reddediliyor, sessizce geçmek yerine.
  • Bir klasörü kendi alt klasörüne taşımak artık engelleniyor, klasör ağacını bozmak yerine.
  • Zengin metin editörünün HTML sanitizer'ı, içine gizlenmiş bir kontrol karakteriyle tehlikeli bir link şemasını (örn. javascript:) korumaya kandırılabiliyordu. Artık değeri kontrol etmeden önce tarayıcının yaptığı gibi normalize ediyor.
  • Dosya yöneticisinde yerel bir HTML dosyasını önizlemek artık onu canlı bir sayfa olarak çalıştırmıyor. Artık tamamen sandbox'lanmış bir frame içinde render ediliyor ve bu dosya türü için "yeni sekmede aç" seçeneği gizlendi — çünkü tarayıcı sekmesinde açmak, sandbox'lı önizlemeden daha az güvenli olurdu.
  • encryption:key artık macOS'ta .env dosyanızın izinlerinin yeniden yazımdan sağlam çıktığını doğrulayamazsa döndürmeyi reddediyor, belirsiz bırakarak döndürmek yerine. Kontrol ettikten sonra yine de devam etmek için --allow-acl-loss verin.

Değiştirildi

  • "Debug Mode" rozeti artık ortama değil role bakıyor — böylece unutulmuş bir APP_DEBUG=true tam da bedelinin ödendiği yerde görünüyor. HandleInertiaRequests, appDebug değerini production'ın tamamında false paylaşıyordu; yani debug çıktısının hâlâ açık olduğunu söyleyen tek uyarı, açık kalmasının asıl önem taşıdığı sunucuda sönüyordu. Kapı artık ortam yerine izleyici: appDebug, config('app.debug') VE izleyenin system_admin rolüne sahip olması, her ortamda. Sistem yöneticisi rozeti production'da görüyor; bayrak başka hiç kimseye gitmiyor, dolayısıyla ortam kontrolünün yazılma sebebi olan parmak izi kaygısı da yerinde duruyor. appEnv production'daki null değerini koruyor — "Dev Mode" rozeti zaten yalnızca local'de çiziliyor. roles ilişkisi auth paylaşımı için hâlihazırda yüklendiğinden kontrol ek sorgu maliyeti getirmiyor. HandleInertiaRequests.php publish edilen bir dosya — kendi kopyasını düzenlemiş uygulama onu korur ve değişikliği elle uygular; ayrıntı UPGRADE.tr.md içinde.

2026-09-10 — v13.7.3

Düzeltildi

  • sk:update artık kendi bağımlılık yükseltmesinin yol açtığı ERESOLVE hatasından nasıl çıkılacağını söylüyor. 13.7.0 frontend araç zincirini güncel sürümlere taşıdı, @tiptap/* dahil (^3.27.1 → ^3.31.3). Uygulamanın package.json'ına birleştirme sorunsuz oturuyor, ama 13.6.x'ten devralınan package-lock.json hâlâ eski grafiği sabitliyor — ve her @tiptap/extension-* paketi @tiptap/core'a tam sürüm peer bağımlılığı tanımladığı için npm ailenin yarısını yükseltemiyor ve ERESOLVE ile duruyor. İki komut da yalnızca npm install && npm run build yazdırıyordu; yani patlayan komutun kendisini. sk:update artık birleştirmenin uygulamada zaten var olan bir kısıtı değiştirdiğini tespit edip yanına lockfile kurtarma satırını basıyor; sk:upgrade de kendi npm install adımı başarısız olduğunda aynı satırı basıyor. Kurtarma UPGRADE.md içinde belgelendi — rm -rf node_modules package-lock.json && npm install; --force ya da --legacy-peer-deps asla, çünkü ikisi de etrafındaki extension'ların derlenmediği bir @tiptap/core kuruyor.

2026-09-08 — v13.7.2

Düzeltildi

  • Geri butonu artık yanındaki oluştur butonundan bir satır yukarıda durmuyor. .admin-page-header sayfa şeridinin kendi mb-6'sını taşıyor ve aura bunu host başına sıfırlıyordu — .sk-card__head--page, .sk-card__actions ve .fm-sidebar__page-header için birer kural. Tablonun kendi başlığı olduğunda header'ı host eden datatable toolbar aksiyon satırı bu listede hiç yoktu, dolayısıyla 24px'lik alt boşluğu header'ı satırın merkez çizgisinden yukarı itiyordu. Sıfırlama artık her host'un verdiği data-sk-page-header işaretine bağlı; hepsine birden uygulanıyor ve eklenecek yeni host aynı boşlukla gelmek yerine sıfırlamayı devralıyor. FileManagerSidebar'a da eksik olan işaret eklendi.

2026-09-08 — v13.7.1

Düzeltildi

  • Datatable toolbar'ındaki geri butonu artık aksiyonlardan önce, diğer tüm kartlarla aynı sırada duruyor. SkDatatable, host ettiği sayfa header'ını (layout'un geri butonu ve sayfa aksiyonları) .sk-dt-toolbar__actions içinde en sona çiziyordu; SkForm'un ve düz kartların geçtiği SkCard ise en başa. İkisini birden taşıyan bir ekranda tabloda [+ Oluştur] [← Geri], formda [← Geri] [Kaydet] diziliyordu. Header artık datatable toolbar'ında da ilk sırada render ediliyor; geri, birincil aksiyonun solunda bir gezinme kontrolü olarak kalıyor, aksiyon gibi okunmuyor. Toolbar'ın aksiyon kapsayıcısı ayrıca SkCard'ın zaten verdiği data-sk-page-header işaretini taşıyor, böylece tüketici CSS'i iki host'u tek seçiciyle hedefleyebiliyor.

2026-09-06 — v13.7.0

Eklendi

  • APP_KEY'den bağımsız, adanmış bir DATA_ENCRYPTION_KEY artık hassas ayar değerlerini (mail.password, storage.spaces_secret, storage.aws_secret, turnstile.secret_key, postman.api_key, apidog.access_token) ve 2FA secret'ları/kurtarma kodlarını koruyor. Daha önce bu verinin tamamı APP_KEY ile şifrelenirdi; bu yüzden bir sunucu taşımasında çalıştırılan rutin bir php artisan key:generate bu veriyi sessizce kurtarılamaz hale getiriyordu — SettingService ortaya çıkan DecryptException'ı yutup hata vermek yerine null döndürüyordu. Yeni anahtarı yönetmek için üç yeni komut var: encryption:key anahtarı üretir ve eski anahtarı DATA_ENCRYPTION_PREVIOUS_KEYS içinde korur; encryption:rekey mevcut satırları, çözemediği bir satıra asla dokunmadan yeni birincil anahtara yeniden şifreler; encryption:health önceki-anahtar listesinin temizlenmesinin güvenli olup olmadığını raporlar ve php artisan sk:doctor buna karşılık gelen bir Data Encryption Key kontrolü kazandı. Benimseme opt-in'dir — bunların hiçbirini çalıştırmayan bir kurulum tıpkı öncekiyle aynı, bayt bayt çalışmaya devam eder; yeni bir sk:install artık anahtarı otomatik üretiyor. Bkz. Veri Şifreleme ve sunucu taşıma runbook'u.

  • SkForm bir reload() metodu ve bir .reloadOnDataUrlChange() builder flag'i kazandı. reload() (defineExpose ile expose edilir), bir host'un dataUrl'i istek üzerine yeniden çekmesine — bir "Yenile" butonu, kardeş bir kaydetme olayı — formu yeniden mount etmeden izin verir. .reloadOnDataUrlChange(true), dataUrl prop'u mount sonrası her değiştiğinde formun otomatik olarak yeniden veri çekmesine opt-in olur (örn. farklı bir kayıt için tekrar kullanılan bir dialog); varsayılan mount-only kalır ki config'i her parent render'da yeniden kurulan bir form her rebuild'de yeniden veri çekmesin. Bkz. Form Builder API.

  • FB.fileUpload() .deferExistingRemoval() ve sürükle-bırak kazandı. .deferExistingRemoval(true), zaten kaydedilmiş bir dosyayı silmeyi anlık bir DELETE /media/{id}'den ertelenmiş bir silmeye çevirir: öğe yalnızca field'ın keep-list'inden çıkar, silme ise save sırasında Lvntr\StarterKit\Traits\HasMediaCollections::syncMediaCollection() üzerinden gerçekleşir. Upload alanının drop zone'u artık üzerine sürüklenen dosyaları da kabul ediyor; bunlar seçici butonuyla aynı accept/maxFileSize/fileLimit doğrulamasından geçiyor. Bkz. Dosya Yükleme Alanı API'si.

  • @lvntr/components paket kütüphanesi artık CI'da lint ediliyor — yeni bir root lint:lib script'i ile (eslint --config stubs/eslint.config.js resources/js/components/Lvntr-Starter-Kit); böylece paylaşılan component kütüphanesindeki bir lint regresyonu, stub tarafında zaten yakalanan aynı şekilde yakalanır.

  • TabIconColor ve TabBadgeSeverity artık TabBuilder core barrel'ından export ediliyor (@lvntr/components/TabBuilder/core), zaten export edilen TabBuilderConfig, TabItemConfig ve TabLayout ile aynı şekilde — bir sekmenin icon rengine veya badge severity'sine karşı tip yazan bir consumer artık doğrudan internal ./types modülüne girmek zorunda değil.

  • TB.tabs() panel mount etme ve URL davranışı için beş yeni chainable seçenek kazandı: .lazy(), .keepAlive(), .history('push' | 'replace'), .urlMode('server' | 'client') ve .syncUrl(boolean). .lazy() yalnızca aktif paneli mount eder (yatay düzende bu PrimeVue'nun kendi lazy modudur), .keepAlive() ise her paneli mount edip inaktif olanları gizleyerek sekme bazlı state'i korur; .history() bir geçişin mevcut history girdisini mi değiştireceğini (varsayılan) yoksa yeni bir girdi mi push edeceğini belirler; .urlMode('client') varsayılan Inertia visit'i yerine sunucuya istek atmadan URL'i günceller; .syncUrl(false) URL senkronizasyonunu tamamen kaldırır. Bkz. tabs.tr.md.

  • SkTabs artık v-model destekliyor ve bir change event'i emit ediyor. Opsiyonel modelValue prop'u, aktif sekme anahtarını hem URL hem local modda iki yönlü bağlar — mount sırasında bir URL deep link'i farklı bir gelen modelValue'nun önüne geçer; change, mount sonrası (ilk mount hariç) her geçişte { key, previousKey, tab } ile tetiklenir.

  • SkTabs bir empty slot'u kazandı; seçilebilir sekme kalmadığında — .permission()/.role()/.visible() yüzünden tüm sekmeler elenmişse ya da görünür sekmelerin hepsi .disabled() ise (eskiden aktif paneli olmayan, tamamen disabled bir şerit kalıyordu) — sidebar veya tab şeridi olmadan tek başına render edilir.

  • Dikey SkTabs artık gerçek bir ARIA tablist'i. Sidebar nav'ı role="tablist"/aria-orientation="vertical" taşır, her sekme butonu aria-selected/aria-controls/aria-disabled ve roving tabindex ile role="tab"'tır, panel ise role="tabpanel" ile sarmalanır; Arrow Up/Down, Home/End enabled sekmeler arasında odağı taşır, Enter/Space seçer.

  • TabsBuilder.build() artık yinelenen sekme key'lerini doğruluyor ve immutable bir snapshot döndürüyor. Yinelenen bir key development build'lerinde hata fırlatıyor, production'da ise sessiz kalmak yerine bir console.error basıyor; TabItemBuilder.build() artık yalnızca eksik değil, yalnızca boşluktan oluşan bir key'i de reddediyor; her build() çağrısı config'in ve sekmelerinin taze bir kopyasını döndürüyor, böylece aynı builder üzerinde sonraki bir .addTabs() ya da döndürülen config'i mutate etmek, zaten build edilmiş bir config'i artık etkileyemez.

  • TabPanelMode, TabHistoryMode, TabUrlMode, TabChangePayload ve SkTabsExposed artık TabBuilder core barrel'ından export ediliyor (@lvntr/components/TabBuilder/core), mevcut TabBuilderConfig, TabItemConfig, TabLayout, TabIconColor ve TabBadgeSeverity export'larıyla birlikte.

  • useUrlTab() artık tabs argümanı için düz bir dizinin yanı sıra bir ref veya bir getter da kabul ediyor, ayrıca yeni bir { history: 'push' | 'replace' } üçüncü argümanı eklendi. Değer her erişimde toValue() ile okunuyor; history: 'push', her geçişe varsayılan 'replace' yerine kendi history girdisini veriyor.

  • CI'de artık src/ üzerinde statik analiz (Larastan/PHPStan, level 5) çalışıyor, Pint'in yanında. phpstan.neon, kitin App\* referanslarının (kitin var saydığı tüketici-sahipli sınıflar, örn. App\Models\User) tam bir tüketici uygulaması olmadan çözülebilmesi için stubs/app'ı tarıyor; phpstan-baseline.neon mevcut bulguları anlık görüntü olarak dondurarak gate'in yalnızca yeni kodu bu çıtaya tabi tutmasını sağlıyor. Yerelde composer analyse ile çalıştırılır. Baseline daha sonra, düzeltilebilir bulgular ayıklanarak 259'dan 238 bulguya daraltıldı.

  • make:sk-domain bir --with-permissions opt-in flag'i kazandı (--with=permissions ile de seçilebilir). Yeni domain'in kaynağını — tüm varsayılan ability'lerle — config/permission-resources.php'ye bir EN görünen adla birlikte kaydeder; TR etiketi ve rol ataması bilinçli olarak elle doldurulmaya bırakılmıştır, çünkü ikisi de yalnızca domain adından güvenle çıkarılamaz. Bkz. artisan-commands.tr.md.

  • CI'de artık bir Playwright admin duman testi paketi çalışıyor. tests/e2e/specs/admin-smoke.spec.ts, scripts/bootstrap-fixture-app.sh ile iskeletlenip scripts/e2e/fixtures/E2EAdminSeeder.php ile seed'lenen gerçek bir fixture uygulamayı login, domain CRUD ve ayarlar ekranları üzerinden sürüyor; yeni bir e2e GitHub Actions job'ı her push'ta fixture'ı kurup paketi headless çalıştırıyor. Yerelde npm run test:e2e ile çalıştırılır. Bkz. testing-e2e.md.

  • sk:install opsiyonel gözlemlenebilirlik (observability) reçeteleri için opt-in bir --modules=telescope,pulse,horizon,sentry flag'i kazandı. Flag verilmezse, bir TTY kurulumu Laravel Telescope, Laravel Pulse, Laravel Horizon ve Sentry'den hangilerinin ekleneceğini sorar; non-interactive bir çalıştırma (veya boş bir --modules=) adımı tamamen atlar, yani bu flag'i kullanmayan bir kurulum için hiçbir şey değişmez. Seçilen her reçete kendi composer require'ını ve post-install komutlarını (örn. telescope:install, horizon:install, vendor:publish --tag=pulse-migrations, vendor:publish --tag=sentry-config) best-effort bir adım olarak çalıştırır — bir reçetenin başarısız olması kurulumu iptal etmez veya diğerlerini engellemez; tam kurulamayan bir reçete sonda elle çalıştırılması gerektiğine dair bir notla listelenir. Telescope dev bağımlılığı olarak değil, normal bağımlılık olarak kurulur: telescope:install, Telescope'un kendi provider'ından türeyen bir provider'ı bootstrap/providers.php dosyasına kaydeder ve bunu üretimdeki bir composer install --no-dev yükleyemezdi.

  • Kitin API dökümantasyonu artık Scramble'ın kendi arayüzü yerine yeni bir bağımlılık olan lvntr/api-dock'u okuma yüzeyi olarak kullanıyor. sk:install her kurulumda config/api-dock.php'yi yayınlıyor; api-dock, panelini /api-dock adresine (ham belge /api-dock/spec) mount ediyor ve kitin route attribute'larından zaten ürettiği aynı OpenAPI belgesini belgeliyor. Yerini aldığı Scramble yüzeyinin aksine, panel artık seed edilen api-docs.read iznine karşı korunuyor — daha önce o docs rotasına hiçbir izin bağlı değildi. Bu koruma kurulum adımının koşmasına bağlı değil: api-dock'un paket varsayılanı paneli, /api-dock/spec'i ve try-it proxy'sini yalnız ['web'] arkasında sunuyor; bu yüzden api-dock.middleware hâlâ o dokunulmamış varsayılandayken kit üzerine ['web', 'auth', CheckApiDocsAccess::class] yığınını kuruyor — middleware sınıfı uygulamada henüz yoksa api-dock'u anonim sunmak yerine tamamen kapatıyor. Beraberinde dört yeni komut geliyor: api-dock:export belgenin bir anlık görüntüsünü alır, api-dock:diff onu kayıtlı bir baseline ile karşılaştırır, api-dock:sync onu Postman/Apidog'a gönderir ve api-dock:agent-guide AI odaklı çıktılar üretir — bir llms.txt ve bir MCP tool export'u (opt-in, ve yalnızca AiTool attribute'u taşıyan operasyonlar için). Bkz. api.tr.md.

  • sk:update ve sk:doctor artık kitin gerektirdiği ama uygulamaya hiç kurulmamış bir Composer paketini tespit ediyor. sk:update eksik paketleri yazdırır ve gerçek bir TTY'de, --dry-run/--no-interaction yokken, composer update lvntr/laravel-starter-kit -W komutunu çalıştırmadan önce sorar; --no-interaction ve --dry-run yalnızca komutu yazdırır. sk:doctor aynı durumu raporlar.

  • Kitin kendi auth ve definitions API operasyonları artık lvntr/api-dock'un AI metadata attribute'larını taşıyor (AiHint, AiPitfall, AiExample, AiTool, ApiFeature). login, üç sonucunun da 200 döndüğünü ve tamamlanmış bir login'i yalnızca token'ın varlığının işaret ettiğini belgeliyor; register, kayıt kapalıyken bilerek 404 değil 403 döndüğünü belgeliyor. Yalnızca me ve definitions lookup AiTool taşıyor, çünkü yan etkisiz tek okumalar bunlar. Yalnızca metadata — davranış veya route değişmedi. Bkz. api-ai-metadata.tr.md.

Güvenlik

  • Local ya da public disk üzerindeki bir FileManager dosyası artık kalıcı, kimlik doğrulaması gerektirmeyen bir public URL'e çözülmüyor. Disk temporary/signed URL desteklemediğinde (her local/public disk desteklemez) daha önce Media::getUrl()'e düşülüyordu — bu link yetkilendirmeyi tamamen atlıyor, bir izin iptalinden ya da dosyanın çöp kutusuna taşınmasından sonra da çalışmaya devam ediyordu. Artık files.download'ın zaten kullandığı aynı kontrolden geçen yeni bir yetkili files.preview route'una düşülüyor; S3 ve temporary URL destekleyen herhangi bir disk etkilenmiyor. Bu route üzerinden yalnızca pasif olarak render edilen tiplerin (görsel, ses, video, PDF, düz metin, CSV) tutucu bir allow-list'i inline sunuluyor — geri kalanı attachment olarak gönderiliyor. Bkz. UPGRADE.tr.md.

  • Logout — ve bir kullanıcının erişimini iptal etmek — artık geride canlı bir OAuth refresh token bırakmıyor. Bir refresh token, kendi access token'ından tasarım gereği daha uzun yaşıyor; ama RevokeUserAccessAction yalnızca hâlâ "canlı" işaretli access token'ları iptal ediyordu. Bu yüzden bir kez logout olmuş ya da bir kez iptal edilmiş bir hesap, o iptal edilmiş access token'a bağlı bir refresh token'ı hâlâ elinde tutup istediği zaman yeni bir access token basabiliyordu. Hem RevokeUserAccessAction hem de stub LogoutUserAction, yeni bir RevokesOAuthCredentials trait'i üzerinden artık önce bir token'a bağlı refresh token'ı, sonra access token'ı iptal ediyor. LogoutUserAction'ı özelleştirdiyseniz bkz. UPGRADE.tr.md.

  • FileManager'da bir dosyayı kopyalamak artık depolama kotasını atlatamıyor. Yalnızca upload yolu storage.quota'yı kullanıma karşı kontrol ediyordu; CopyFileAction hiçbir tavan olmadan kopya başına tam bir yeni blob oluşturuyordu. Artık fiziksel kopyadan önce aynı kota kontrolünü çalıştırıyor ve upload yolunun kullandığı aynı quota_exceeded mesajıyla reddediyor.

  • DATA_ENCRYPTION_KEY rotasyonu artık .env üzerindeki dosyaya özel bir ACL'i sessizce ne düşürüyor ne de kazandırıyor. .env üzerinde bir setfacl/chmod +a izni fileperms() için görünmez; sahip/grup/mode hayatta kalırken bu izin atomic rename'i atlatamıyordu. encryption:key artık ACL'i taşıyor ve geri okuyarak doğruluyor; taşınamıyorsa rotasyonu reddediyor. Ayna durum da kapatıldı: dizin seviyesinde bir inheritance kuralı, anahtar henüz yazılmadan geçici dosyaya .env'in hiç sahip olmadığı bir ACL koyabiliyordu — bu miras alınan girdi artık yazımdan önce normalize ediliyor. İki yöndeki uyuşmazlık da reddediliyor; yeni --allow-acl-loss bayrağı bu reddi, durumu elle uzlaştıracak bir operatör için uyarıya düşürüyor.

  • stubs/package.json'daki @tiptap/* paketleri ^3.22.4'ten ^3.31.3'e taşındı, upstream düzeltmeleri almak için; stubs/package-lock.json yeniden üretildi. Bu aralıkta kitin kullandığı hiçbir API değişmedi.

  • Dosya yüklemeleri artık kabul edilen MIME tiplerinden türetilen bir uzantı allow-list'ine karşı da kontrol ediliyor; media-library'nin aktif-içerik uzantıları da artık koşulsuz engelleniyor. FileManager'ın public disk'i saklanan bir dosyayı kendi client adıyla geri sunuyor ve mevcut mimetypes: kuralı yalnızca sniff edilen içeriğe bakıyordu — bu yüzden byte'ları kabul edilen bir tipe uyan ama client adı .html ile biten bir dosya daha önce olduğu gibi kaydedilip aktif içerik olarak sunulabiliyordu. UploadFileRequest, yeni bir mimeExtensionMap() üzerinden bir extensions: kuralı türetiyor ve mevcut sk-file-manager.errors.upload_invalid_type mesajını yeniden kullanıyor; böylece örneğin payload.html adlı bir yükleme, byte'ları geçerli kabul edilen bir tip olsa bile 422 ile reddediliyor. Yayınlanan UploadAvatarRequest stub'ı da aynı extensions:jpg,jpeg,png,webp kuralını taşıyor — mevcut kurulumlar bunu php artisan sk:update ile ya da kuralı elle ekleyerek alır. media-library.disallowed_extensions artık opt-out olmadan koşulsuz sertleştiriliyor ve html, htm, xhtml, xht, svg, svgz, xml, xsl, xslt, js, mjs, hta'yı da engelliyor: Spatie, client adının yalnızca son değil her nokta segmentini kontrol ediyor; bu yüzden name.html.pdf gibi çift bir uzantı, .pdf tek başına request seviyesindeki kuralı geçse bile bu katmanda reddediliyor. Spatie'nin kendi FileNameNotAllowed istisnası artık belirsiz bir 500 yerine 422 ("The uploaded file name is not allowed.") olarak dönüyor. Aynı segment bazlı kontrol artık UploadFileRequest'in kendi içinde de çalışıyor (istisna yolu yerine alan seviyesinde 422; kurulu media-library sürümünden bağımsız) ve bir dosyayı yeniden adlandırmak artık uzantısını değiştiremiyor: RenameFileRequest, yeni adın mevcut uzantıyı (büyük/küçük harf duyarsız) korumasını zorunlu kılıyor ve yasaklı bir segment içeren adı reddediyor — media library file_name değişince saklanan dosyayı fiziksel olarak yeniden adlandırdığından, .html'e rename doğrulanmış bir görseli aktif içeriğe çevirirdi. mimeExtensionMap() dışındaki MIME tipleri (admin'in eklediği PPTX, RAR, Markdown, …) uzantılarını artık ham alt tip yerine Symfony'nin MIME veritabanından çözüyor; text/plain da .txt, .log, .md, .csv, .json, .ini, .yml gibi metin adlarını kabul ediyor. Spatie'nin FileIsTooBig istisnası (request max: kuralından bağımsız kendi media-library.max_file_size tavanı, varsayılan 10 MB) da artık 422 olarak dönüyor; yayınlanan SettingsServiceProvider stub'ı bu tavanı FileManager'ın max_size_mb ayarıyla hizalıyor. Kit artık spatie/laravel-medialibrary ^11.23 istiyor (segment bazlı engelleme 11.23.0 ile geldi); composer update listeyi uygulayan bir build'i çeker. Bkz. UPGRADE.tr.md.

  • Ayarlar cache'i artık şifrelenmiş değerler için çözülmüş düz metin yerine ciphertext saklıyor. SettingService::allGrouped(), tamamen çözülmüş satır kümesini bir saat boyunca settings anahtarı altında cache'liyordu; bu yüzden herhangi bir cache backend'i (Redis, bir dosya store'u, başka bir uygulamayla paylaşılan bir store) mail.password, storage secret'ları ve benzeri değerleri o saat boyunca düz metin olarak tutuyordu. Ham satırlar artık yeni bir settings:v2 anahtarı altında cache'leniyor ve her cache okumasından sonra çözülüyor; eski settings anahtarı yeni snapshot ilk kurulduğunda unutuluyor, php artisan encryption:rekey ise satırları yeni anahtara yeniden şifreledikten sonra aynı settings:v2'yi temizliyor. Çözülemeyen şifreli bir satır (rotasyona uğramış ya da yanlış bir DATA_ENCRYPTION_KEY, bozuk ciphertext) artık her okumada — loglanarak — yeniden deneniyor; tam bir saat boyunca null olarak cache'lenmek yerine, doğru anahtarı geri koymak cache'in süresi dolmasını beklemeden bir sonraki istekte etkili oluyor.

  • API login endpoint'i artık mevcut IP-başına limitin üstüne kendi hesap-başına rate limitine de sahip. POST /api/v1/auth/login, yeni bir api-login limiter'ına taşındı: IP başına dakikada 5 (değişmedi) artı e-posta adresi başına dakikada 3 yeni — dolayısıyla tek bir hesap için denemelerini birçok IP'ye yayan bir saldırgan artık da durduruluyor. Bu limiter, yalnız ayrı web login rotasını etkileyen auth.login_throttle ayarıyla asla gevşetilemez. Kaydı publish edilen FortifyServiceProvider'da değil paketin kendisinde yapılıyor; böylece sk:update publish edilmiş dosyalardan birini yenileyip diğerini koruduğunda limiter asla kaybolmuyor. Publish edilmiş routes/api/public-api.php'yi özelleştirdiyseniz, rota değişikliğini elle taşımak için UPGRADE.tr.md'ye bakın. Üç /api/v1/auth/* endpoint'i daha önce isimsiz tek bir IP kovasını paylaştığı ve login artık kendi kovasına sahip olduğu için, grubun toplam IP-başına payı dakikada 5'ten 10'a çıkıyor; login rotasının kendisi IP başına 5/dakikada değişmedi.

  • Yeni bir opt-in bayrak, admin panelinizin script etiketini 'unsafe-inline' yerine istek-başına bir CSP nonce'ıyla sunabilir; bu da enjekte edilmiş bir inline <script>'in çalışmasını kapatır. Açmak için STARTER_KIT_CSP_NONCE=true ayarlayın. Varsayılan olarak false'tur çünkü publish edilmiş resources/views/app.blade.php'nizde nonce="{{ Vite::cspNonce() }}" özniteliğini gerektirir — bu olmadan bayrağı açmak panelin tema script'ini sessizce kırar. Yepyeni bir sk:install her iki parçayı da zaten yerine koyar. Mevcut bir kurulumda bunu açmadan önce UPGRADE.tr.md'ye bakın.

Değişti

  • Başlıklı SkDatatable toolbar'ı artık iki satıra ayrılıyor: üstte başlık ve butonları, altta arama ve filtreler. DB.table().title(), başlığı, arama kutusunu, satır içi filtre pill'lerini, filtre/kolon butonlarını, create butonunu, #toolbar / #toolbar-start slot'larını ve — aura temasında — sayfa header'ını tek satıra diziyordu; tabloda birkaç filtreden fazlası olduğunda satır yetmiyordu. Başlık satırının padding'i artık .sk-card__head ile birebir aynı (solda başlık/alt başlık, sağda create butonu ve toolbar slot'ları), yani başlıklı bir tablo normal bir kart başlığı gibi okunuyor; arama, satır içi filtre pill'leri ve filtre/kolon grubu ise altındaki kendi satırına iniyor. Başlıksız toolbar aynı kaldı — tek satır, aksiyonlar en sonda.

  • Aura temasında kart yüzeyli SkDatatable, sayfa başlığını artık üstündeki ayrı bir şerit yerine kendi toolbar'ında çiziyor. Layout'un sayfa header'ını — başlık, alt başlık, geri butonu ve #page-actions — sarmalayan SkCard üstleniyor ve tablo toolbar'ının hemen üstünde kendi başlık satırı olarak çiziyordu; Kullanıcılar, Roller, Aktivite Kayıtları ve Log Dosyaları ekranlarında önce bir başlık bloğu, altında ayrı bir toolbar görünüyordu. Header'ı artık tablonun kendisi üstleniyor ve toolbar'ın başlık satırı olarak render ediyor — başlıklı toolbar'ın zaten başlık ile arama/filtre satırı arasına koyduğu ayıracın üstünde. Böylece başlık, sayfa aksiyonları ve tablonun kendi kontrolleri tek bir header gibi okunuyor. DB.table().title() taşıyan tablo aynı kaldı (kendi başlığı yerinde durur, üstlenilen header yalnızca geri butonu ve sayfa aksiyonlarını katar); sayfa header'ını yerinde çizen ve zaten karta devretmeyen main teması etkilenmedi. Aktivite Kayıtları ve Log Dosyaları ekranları artık gereksiz kalan .title() çağrısını bıraktı — başlıkları diğer ekranlar gibi sayfadan geliyor.

  • Yatay SkTabs artık sekme şeridini kendi kartının içinde çiziyor. Şerit, panelin üstünde sayfa zemininde çıplak duruyordu; artık bir SkCard içinde ve aura temasında layout'un sayfa header'ını da bu kart üstleniyor — böylece sekmeli ekran tek blok halinde okunuyor: başlık, alt başlık, ayıraç, sekme satırı, sonra panel. Sekmeler PrimeVue'nun varsayılan görünümünü bıraktı: düz ikon + etiket, pasifken soluk, aktifken primary renk ve tek gösterge olarak 2px alt çubuk. Paneller kendi kartlarını aynen koruyor, dikey düzen değişmedi. Kendi stilini yazdıysan tek sınıf adı değişikliği: yatay sekme ikonu artık .sk-vtab__icon değil .sk-tabs__icon (dikey nav eskisini kullanmaya devam ediyor).

  • Aura teması artık sayfa başlığını üst bara taşımıyor; AdminLayout tek page header'ını içerik kartının kendi head'ine devrediyor. Aura başlığı, alt başlığı ve geri butonunu AdminHeader'a devrediyordu; bu da içeriğin ilk kartını başlıksız bırakıyor ve başlık ya da aksiyon isteyen her sayfayı kendi tema dalına zorluyordu — Users/Roles index sayfaları datatable'ın .title()'ını kuruyor ve create butonunu yalnızca aura'da tablo toolbar'ına taşıyordu, ApiRoutes/Index ise aura'ya özel komple bir başlık kartını elle yazıyordu. .title() çağrısı koşulsuzdu, dolayısıyla main teması sayfa başlığını iki kez basıyordu. Hepsi kaldırıldı. AdminLayout artık tek bir AdminPageHeader çiziyor — başlık, alt başlık, geri butonu, #page-actions — ve onu aktif temaya devrediyor: main'de yerinde kalıp içeriğin üstündeki şerit olarak duruyor, aura'da ise başlığı talep eden ilk SkCard onu kendi ayrı head satırında çiziyor (datatable kartı, form kartı, aktif sekmenin kartı). Devri SkCard yönetiyor: uygun kartlar mount sırasına göre aday listesine yazılıyor ve listede kalan ilk kart başlığı çiziyor; böylece sekme değişimi ya da Inertia gezinmesi — gelen kartın giden karttan önce mount olduğu durum — başlığı doğrudan devrediyor, kartın dışına düşürmüyor. Toolbar'ında başlık taşıyan SkDatatable (DB.table().title()) header'ı o toolbar'da barındırıyor ve sarmalayan kartı devre dışı bırakıyor; böylece tablo başlığı sayfa başlığının altına düşmüyor. Kendi başlığı olan kart başlığını koruyor ve page header'ı aynı head satırına katlıyor — sadece geri butonu ve sayfa aksiyonları, kart başlığının üstünde ikinci bir sayfa başlığı yok. Başlığı olmayan kart (datatable kartı, düz form kartı) ise header'ı kendi ayrı head satırında alıyor. Transparan sarmalayıcılar ve dialog gövdeleri hiç kaydolmuyor; chrome kartlar host-page-header="false" ile dışarıda kalıyor — dikey SkTabs navigasyon kartı, AvatarUpload ve FormBuilder section'ı, böylece başlık gerçek içerik kartına iniyor, sayfanın kenar çubuğuna değil. Kendi kart yüzeyini elle yazan kit ekranları (ayarlardaki Görünüm sekmesi, API rotaları sayfası, log görüntüleyici) artık SkCard kullanıyor; aura'da başlık her ekranda aynı yerde duruyor. Sayfa bileşeni aktif temaya göre dallanmıyor. Dosyalar ekranı hiç SkCard çizmiyor — dosya yöneticisi kendi yüzeyi — bu yüzden başlığı FileManager.vue kendisi talep ediyor ve kenar çubuğunun en üstünde, Yeni dosya yükle butonunun üstünde çiziyor; aura artık yöneticinin üstündeki çıplak şeride düşmüyor. Tüketici sayfaları için kırıcı: AdminLayout'un header-in-card prop'u ve usePageHeader() composable'ı (resources/js/composables/usePageHeader.ts) kaldırıldı — prop'u silin ve pageHeader.active okuyan #title-end geri butonlarını kaldırın; bu butonu artık layout çiziyor. AdminHeader'ın page-title, page-subtitle, show-back prop'ları ve back event'i de birlikte kaldırıldı. Adaylık iki eksende de reaktif: Görünüm ekranından tema değiştiğinde kartlar mount hâlde kalsa bile yeniden karar veriliyor (eskiden setup'ta donuyordu ve aura → main geçişinde başlık iki kez çiziliyordu), ve mount hâlde ama pasif bir sekme panelindeki kart adaylıktan çekiliyor (yatay SkTabs ya da panels: 'all'), böylece başlık gizli panelle birlikte kaybolmak yerine görünür panele geçiyor. AdminHeader.vue eski opsiyonel pageTitle/pageSubtitle/showBack prop'larını hâlâ kabul ediyor ve back olayını hâlâ yayıyor: sk:update bu dosyayı ve AdminLayout.vue dosyasını birbirinden bağımsız yeniliyor, dolayısıyla layout'u özelleştirmiş bir uygulama bunları hâlâ bağlayan eski layout'ta kalıyor.

  • Dikey SkTabs sekme butonları artık açık bir role taşımamak yerine role="tab" taşıyor, panel içeriği de kart gövdesi içinde yeni bir role="tabpanel" <div> ile sarmalanıyor. Bir sekme butonunu getByRole('button') ile seçen bir test getByRole('tab')'a geçmeli; kart gövdesi altında direkt-child selector'a dayanan özel CSS'in bir kontrolü hak edebilir.

  • TabsBuilder.build() artık development build'lerinde yinelenen bir sekme key'inde hata fırlatıyor, production'da ise sessiz kalmak yerine aynı mesajı console.error ile basıyor. Yinelenen key'ler daha önce slot çözümlemesini ve URL seçimini sessizce bozuyordu — ikinci sekme birincinin içeriğini render ediyor ve ?tab= ile asla erişilemiyordu.

  • SkTabs artık @/composables'daki yayınlanmış useUrlTab kopyasını import etmiyor; bunun yerine kendi internal, eşdeğer bir aktif-sekme state'ine sahip. Bu bir version-skew riskini ortadan kaldırıyor (sk:publish --tag=composables, bir uygulamayı shipped bileşenin beklediğinden daha eski bir useUrlTab ile bırakabiliyordu), ama yayınlanmış useUrlTab.ts'ini özellikle SkTabs davranışını değiştirmek için elle düzenlemiş bir proje bu düzenlemenin artık etkili olmadığını görecek — useUrlTab()'ın kendisi, onu doğrudan çağıran uygulama kodu için etkilenmiyor.

  • Sayfalar-arası "tümünü seç" toplu seçim artık desteklenmeyen bir filtrede sessizce düşürmek yerine fail-closed davranıyor. BulkFilterSnapshot::normalize(), uygulayamadığı aktif bir filter[...] anahtarını snapshot'tan düşürmek yerine 422 (sk-bulk.unknown_filters) ile reddediyor; önceki davranış tablonun gösterdiğinden daha geniş bir küme çözüyor ve toplu işlemin düşürülen filtrenin gizlediği satırlara ulaşmasına izin veriyordu. Gönderilen UserBulkSelectionQuery ve RoleBulkSelectionQuery'yi etkiler. Yalnızca null değer ya da boş dizi pasif sayılır — Spatie'nin AllowedFilter'ının atladığı iki şekil; boş ya da yalnızca boşluktan oluşan bir string olduğu gibi geçirilir ve tablonun kendi koşuluyla uygulanır (exact bir filtre tablonun gösterdiği aynı boş kümeyi verir, search/tarih sınırları onu yok sayar), böylece boş bir değer de toplu kümeyi asla genişletemez.

  • DatatableQueryBuilder::columns() payload şekillendirmesi fail-closed. Hiçbir tanımlı sütunla eşleşmeyen bir ?columns= istek parametresi önceden tam satıra geri dönüyordu; artık her satırı yalnızca alwaysInclude() anahtarlarına indirgiyor — "tanımlı sütun anahtarları frontend ile eşleşmeli" sözleşmesiyle uyumlu.

  • TB.tabs().queryParam() artık boş veya yalnızca boşluktan oluşan bir ismi reddediyor. Development build'lerinde hata fırlatıyor, production'da console.error basıp önceden ayarlı ismi (tab varsayılanı ya da önceki çağrı) koruyor — boş isim eskiden olduğu gibi saklanıyor ve hiçbir şeyin geri okuyamadığı bir ?=key URL parametresi üretiyordu; sekmeler sessizce URL ile senkronu kaybediyordu.

  • Kurulum komutları artık zorunlu bir adım başarısız olduğunda sıfırdan farklı bir çıkış kodu döndürüyor. sk:install, sk:update, sk:upgrade (ve yayınlanan site:install stub'ı) migrate, db:seed, vendor:publish, sk:seed-permissions, passport:keys, key:generate, composer dump-autoload, npm install ve npm run build çağrılarının sonucunu hiç okumuyordu: bozuk bir bağlantıda ölen migrate yine DONE yazıyor, resume checkpoint'i adımı tamamlanmış olarak kaydediyor, stub-hash registry'si yazılıyor ve komut 0 ile çıkıyordu — CI, yarım kurulmuş bir uygulamanın üzerinden yeşil geçiyordu. Artık her alt komut sonucu denetleniyor; başarısız bir zorunlu adım (publish, migration, seeder, izin tohumlama, Passport anahtarları, şifreleme anahtarları) koşuyu durduruyor, checkpoint'i beklemede bırakıyor (sk:install --resume kaldığı yerden devam ediyor), registry yazımını atlıyor ve başarısız adımı + devam komutunu adlandıran tek satırla bitiyor. Frontend adımları bilerek ölümcül değil — npm install, Wayfinder üretimi ve npm run build (ayrıca composer dump-autoload ve cache temizlikleri) yalnızca uyarıyor, elle çalıştırılacak komutu yazıyor ve kapanış özetinde tekrar listeleniyor; böylece Node ya da composer'ı olmayan bir makine bugünkü gibi kurulmaya devam ediyor. Sessizce başarısız olan bir migration'la şu an geçen bir CI hattı artık kırmızıya dönecek — bkz. docs/UPGRADE.tr.md. site:install değişikliği bir stub'ta yaşıyor: yalnızca yeni kurulumlara ve sk:update ile tazelenen uygulamalara ulaşır, mevcut tüketici kopyası olduğu gibi kalır.

  • Kırıcı değişiklik: kitin API dökümantasyonu Scramble'ın varsayılan /docs/api adresinden yeni api-dock paneline, /api-dock'a taşındı. Scramble'ın kendi rotaları artık bilerek devre dışı bırakıldı, yani /docs/api ve /docs/api.json güncelleyen her uygulamada 404 dönüyor. Eski URL'i yer imlerine ekleyen veya ona link veren her şeyin /api-dock'a taşınması gerekiyor; kendi config/scramble.php'sini yayınlamış bir tüketici, artık rakip bir docs rotası kaydetmediğinden emin olmak için o dosyayı kontrol etmeli. Bkz. UPGRADE.tr.md. Kapatma işlemi artık yerine geçecek olanın gerçekten var olmasına bağlı: Scramble::ignoreDefaultRoutes() çağrısı Lvntr\ApiDock\ApiDockServiceProvider sınıfının varlığını da şart koşuyor. Böylece bu sürümün kaynağını taşıyıp Composer'ın yeni bağımlılığı çözmesine izin vermemiş bir kurulum — dev-main izleyen bir path/VCS repository'si, eski bir lock'tan geri yüklenmiş bir vendor/, api-dock'u dışarıda bırakan bir kısıt — hiç dökümantasyon yüzeyi kalmaması yerine Scramble'ın rotalarını koruyor (o pencerede api-dock da kayıtlı değildir, API Routes ekranı da aynı eksik rota yüzünden panel butonunu gizler). Normal yükseltme yolu etkilenmiyor: composer update lvntr/laravel-starter-kit api-dock'u kitle birlikte kuruyor.

  • @tiptap/extension-task-item ve @tiptap/extension-task-list, stubs/package.json'dan kaldırıldı — hiçbiri kitin kendi kodunda hiçbir yerde import edilmiyor. Kendi kodunuz bunları doğrudan import ediyorsa uygulamanızın kendi package.json'ına ekleyin.

  • stubs/app/Models/User.php'ın @property docblock'u, modelin gerçek kolonlarına ve ilişkilerine uyacak şekilde düzeltildi. Runtime davranışı değişmedi.

  • phpstan-baseline.neon, 259'dan 238 bulguya daraltıldı — bir avuç gerçekten düzeltilebilir bulgu (bayat docblock'lar, ulaşılamayan dallar) taşınmak yerine düzeltildi.

  • CI'nin gerçek veritabanı job'ları (MySQL, MariaDB) artık yalnızca raporlamıyor, merge'leri de engelliyor — bir sürüm boyunca yeşil kaldıktan sonra.

  • release.sh artık GitHub CLI (gh) ve etiketlenecek tam commit için yeşil bir uzak CI koşusu istiyor, yerel kalite kapısından önce kontrol ediliyor. Ayrıca artık composer analyse ve tam bir frontend kapısı (build/typecheck/lint/test) çalıştırıyor. Yayın akışı değişti: ./release.sh çalıştırmadan önce origin main'e push edip CI'nin yeşillenmesini bekleyin. Bu yalnızca repo tooling'i — composer update veya sk:update'i etkilemiyor.

  • sk:doctor'ın 22 kontrolü artık isim, mesaj ve ipucu metinlerini PHP içinde sabit kodlamak yerine sk-doctor.* lang key'lerinden çözüyor. src/Console/Doctor/Checks/ altındaki her sınıf kullanıcıya görünen metni __('sk-doctor.<kontrol>.<durum>', [...]) ile çağırıyor; böylece hem admin panelindeki Sistem Sağlığı ekranı hem de php artisan sk:doctor artık her zaman İngilizce basmak yerine uygulamanın aktif dilini takip ediyor. İngilizce metin byte-byte değişmedi; bir tüketici uygulaması app/lang/<locale>/sk-doctor.php yayınlayıp yalnızca ilgili key'i yeniden tanımlayarak tek bir string'i override edebilir — sk-bulk.* ve benzerlerinin zaten kullandığı namespace'siz arama sayesinde uygulama kopyası paketinkinin önüne geçiyor. sk:doctor --only=<seçici> çeviriden etkilenmiyor: seçiciler artık kontrolün görünen adından değil SINIF adından türetiliyor (DatabaseConnectionCheck → database-connection), yani her dilde aynı kalıyor; sınıf adıyla örtüşmeyen üç belgeli seçici (filemanager-disk, permission-matrix, unresolved-routes) takma ad olarak çalışmayı sürdürüyor.

  • stubs/package.json içindeki frontend build/lint/test araçları güncel sürümlerine taşındı: @types/node ^26.5.0, concurrently ^10.0.5, eslint-plugin-vue ^10.11.0, typescript-eslint ^8.70.0 ve unplugin-vue-components ^32.1.0. Hiçbiri yayınlanan bundle'a girmiyor — runtime bağımlılığı değişmedi, komponent kaynağına dokunulmadı — ancak unplugin-vue-components bir major atladığı için tüketicinin kendi Vite build'ini etkiliyor; bu yüzden kapının tamamı yeniden koşturuldu: vue-tsc --noEmit, hem stubs hem @lvntr/components kütüphanesi üzerinde ESLint, 290 testlik Vitest paketi ve her iki Vite build'i (client ve SSR).

Düzeltildi

  • oauth_device_codes.user_id onarım migration'ının geri alınması artık hiç dokunmadığı bir kurulumun şemasını yeniden yazmıyor. İleri adım koşullu — yalnızca legacy bigint user_id'yi uuid'ye çeviriyor, create migration'ın zaten foreignUuid tanımladığı taze kurulumda hiçbir şey yapmıyor. down() koşullu değildi: kolon uuid okunduğu her durumda bigint'e geri çeviriyordu, ki batch koştuktan sonra bu her iki başlangıç noktası demek. MySQL bu dönüşümü sessizce kabul ediyordu; uuid()'nin char(36) yerine native 16-baytlık tipe derlendiği MariaDB 10.7+ ise doğrudan reddedip (Cannot cast 'uuid' as 'bigint unsigned' in assignment) rollback'i düşürüyordu. down() artık bilinçli olarak no-op: ileri adımın null'ladığı değerler tanımı gereği geçersiz kimliklerdi ve hiçbir tip değişikliği onları geri getirmiyor, tam batch rollback'i tabloyu zaten create migration üzerinden düşürüyor, tek adımlık rollback ise kolonu uuid bırakıyor — ileri adım da onu zaten-onarılmış okuyup atlıyor.

  • Ekranı aşan admin sayfaları artık alttan kesilmek yerine yeniden scroll oluyor. Scroll kabı olan .admin-content çocuklarını flex sütun olarak diziyor, yani her sayfa kökü küçülebilir bir flex öğesi — ve kendi taşmasını kırpan bir çocuk (.sk-card overflow: hidden taşır) otomatik minimum boyutunu içerik yüksekliği yerine 0 olarak çözüyor. Uzun bir form kartı bu yüzden artakalan ekran yüksekliğine sıkıştırılıp kendi alanlarını kırpıyordu; scroll kabında da kaydırılacak bir şey kalmadığı için sayfa donmuş gibi görünüyor, son bölüm ve alttaki butonlar erişilemiyordu. Scroll kabının çocukları artık min-height: min-content taşıyor; sayfa doğal yüksekliğini koruyor ve kayıyor. Ekranı bilinçli olarak dolduran kökler (dosya yöneticisinin min-h-0 flex-1 kabuğu) etkilenmedi, gövde yüksekliğini zaten aynı scroll kabına göre sınırlayan datatable ekranları da birebir aynı render ediliyor.

  • sk:doctor artık Horizon çalışırken doğrulanamayan queue worker uyarısı vermiyor. Queue Worker kontrolü tüm async driver'ları aynı sayıyordu, bu yüzden redis kuyruğu her zaman "worker liveness cannot be verified automatically" raporluyordu — tam da bu canlılığı kendisi yayınlayan Laravel Horizon'un kurulu olduğu birçok kurulumda bile. redis kuyruğunda kontrol artık Horizon kuruluysa Horizon'un master supervisor kaydını okuyor: çalışan bir master supervisor sayısıyla birlikte OK veriyor, tamamı duraklatılmışsa horizon:continue'yu gösteren bir uyarı veriyor, hiç master yoksa Horizon'un kurulu ama başlatılmamış olduğunu uyarıyor, durum okunamıyorsa (örneğin Redis kapalıysa) altta yatan hatayla uyarıyor. Horizon bağımlılık olmuyor — sözleşmesi interface_exists() koruması ardında FQCN string'i olarak çözülüyor — dolayısıyla Horizon'suz bir kurulum ya da sqs/beanstalkd üzerindeki bir kurulum önceki genel uyarıyı aynen almaya devam ediyor.

  • sk:install artık mevcut users tablosu kitin şemasını taşıyamadığında bir foreign key üzerinde ölmek yerine sebebi adıyla söylüyor. Kit users tablosunu uuid ile anahtarlar; kit kurulmadan ÖNCE php artisan migrate çalıştırmış stock bir Laravel uygulamasında users bigIncrements id taşır — üstelik stock Laravel'in users migration'ı deftere kitin yayınladığı dosya adının aynısıyla (0001_01_01_000000_create_users_table.php) kaydedilmiştir, yani kitin uuid sürümü hiç çalışmaz. Uyumsuzluk birkaç migration sonra file_folders.created_by ile users.id'yi adlandıran çıplak bir SQLSTATE[HY000] ... 3780 olarak ortaya çıkıyor, içinde operatörün eyleme dönüştürebileceği hiçbir şey olmadan. Migration adımı artık strateji sunmadan önce mevcut users.id'yi yokluyor: sebebi ve çözümü yazıyor, ek yapıcı seçeneği WILL FAIL diye işaretliyor ve yine de o seçilirse (ya da oturum prompt açamıyorsa) adımı bu gerekçeyle durduruyor. migrate:fresh'i koruyan satır yoklamasının aksine bu yoklama fail-open'dır — okunamayan bir şema asla uyumsuzluk sayılmaz, yani cevaplayamadığı bir soru yüzünden bir kurulumu bloklayamaz. Çözüm tam bir sıfırlamadır: migrate:fresh, sunulduğunda fresh seçeneği ya da boş bir veritabanı, ardından sk:install --resume.

  • sk:install artık mevcut bir proje için kurtarma yolu olarak belgelenmiyor. docs/install.tr.md ve docs/update.tr.md komutu idempotent bir proje-geneli onarım gibi tarif ediyordu. Değil: hash kaydı düzenlediğiniz değil sildiğiniz dosyayı korur ve --force verilmeden consumer tarafından düzenlenmiş bir published dosya tazelenmek yerine atlanıp raporlanır, --force ile ise doğrudan üzerine yazılır — ikisi de sk:update ya da kapsamı daraltılmış sk:publish --tag=<alan>'ın verdiği seçici, düzenlemeyi koruyan tazelemeyi vermez. Her iki rehber de artık uyarıyı taşıyor, sınır UPGRADE.tr.md içinde kayıtlı.

  • sk:install artık kendisinin kurmadığı bir uygulamada çalışmayı reddediyor. Komut, bir projenin zaten kurulu olduğunu anlamak için yalnızca kendi hash kaydına (storage/starter-kit/hashes.json, git tarafından yok sayılır) güveniyordu; bu kaydın kaybı canlı bir uygulamayı yepyeni gösteriyordu. Artık banner'dan önce fail-closed bir tespit adımı çalışıyor — kit'in şema tabloları ve yalnızca kuruluma özel yollar kontrol ediliyor; bunlardan biri eşleşen bir kayıt olmadan bulunursa komut hiçbir şey yazmadan duruyor. Çıkış yolu olarak sk:update ve yeni php artisan sk:install --adopt (yalnızca kaydı yeniden inşa eder, --dry-run önizler) adlandırılıyor; --force yine devam ediyor ama artık ilk kurulum sayılmıyor.

  • Mevcut bir .env, ne ilk kurulumda ne yeniden çalıştırmada sk:install tarafından üzerine yazılmıyor. İlk kurulum, mevcut .env'in üzerine .env.example'ı doğrudan kopyalıyordu; bu DB_PASSWORD, APP_KEY ve yapılandırılmış her şeyi yok ediyordu. Installer artık merge ediyor: eksik .env.example anahtarları ekleniyor, ilk-kuruluma-özel anahtarlar ise yalnızca yoksa tohumlanıyor; mevcut hiçbir değer yeniden yazılmıyor. .env, yalnızca hiç yoksa .env.example'dan oluşturuluyor.

  • Consumer tarafından değiştirilmiş bir published dosya artık yalnızca sk:update'in değil, sk:install'in yeniden-publish yolunun da atladığı bir dosya. İki komut da aynı üç-yönlü stub/hedef/kayıt-hash karşılaştırmasını paylaşıyor; son kaydedilen hash'le artık eşleşmeyen bir dosya bir consumer düzenlemesi sayılıp sessizce üzerine yazılmak yerine atlanıp raporlanıyor. Opt-out yine --force.

  • sk:install artık yeniden kurulumda izlenmeyen bir dosyanın üzerine yazmıyor. Hash kaydında hiç izi olmayan bir dosya — çünkü yeni bir paket sürümü, bu uygulamada daha önce hiç göndermediği bir yola dosya göndermeye başlamıştı — --force fark etmeksizin üzerine yazılıyordu. Artık bir consumer düzenlemesiyle aynı muameleyi görüyor: --force verilmedikçe korunup raporlanıyor. Bu koruma yalnızca bir kayıt varken geçerli; gerçek bir ilk kurulum izlensin izlenmesin her yolu yine yayınlıyor.

  • sk:install'in yıkıcı migrate:fresh seçeneği artık bir select() yanıtı değil, yazılı bir onay istiyor. Kurulum-anı menüsü "tüm tabloları sil ve migration'ları sıfırdan çalıştır" seçeneğini sıradan bir evet/hayır tercihi olarak sunuyordu; geri dönüşü olmayan bir sıfırlamadan yalnızca bir yanlış tuşa basma kadar uzaktaydı. Bu seçeneği seçmek artık veritabanı adını (ya da fresh kelimesini) bir text() prompt'unda yazmayı istiyor; bunun dışındaki her yanıt — boş bir cevap dahil — hiçbir şey silinmeden ek türden migrate yoluna düşüyor. Seçenek ayrıca APP_ENV production benzeri göründüğünde, APP_DEBUG kapalıyken, oturum prompt gösteremediğinde ya da mevcut bir tablo zaten satır içeriyorsa baştan sunulmuyor.

  • Oturumu hâlâ açıkken deaktive edilen bir hesap artık bir sonraki istekte oturumdan atılıyor. Login yolu zaten aktif olmayan bir hesabı reddediyordu, ama zaten açık bir oturuma erişemiyordu. Yeni EnsureUserIsActive middleware'i her web/api isteğinde status'u kontrol ediyor ve operatörün deny-list'iyle (starter-kit.security.active_status_denied, varsayılan ['inactive', 'banned']) eşleştiğinde oturumu sonlandırıyor; her belirsiz durumda (status attribute'u yok, string olmayan değer, listelenmemiş bir status) kasıtlı olarak fail-open ve starter-kit.security.enforce_active_status = false ile tamamen devre dışı bırakılabiliyor.

  • Çözülemeyen bir ayar artık hiç ayarlanmamış bir ayardan sessizce ayırt edilemez değil. SettingService çözme sırasında her Exception türünü yakalayıp null dönüyordu; allGrouped() de bunu bir saate kadar cache'liyordu — yani yanlış anahtar, bozuk payload ya da hatalı yapılandırılmış cipher, mail/storage/Turnstile alanlarında sessizce env/varsayılan değere düşüyordu. Artık yalnızca DecryptException ele alınıyor (yine null, ama ciphertext yazılmadan loglanıyor); diğer her şey yukarı fırlıyor.

  • encryption:health artık config'i önbelleklenmiş ve bayat olduğunda fail-closed davranıyor. config:cache altında komut "cache bir .env düzenlemesinden önce alınmış" ile "değer hiçbir zaman env'den gelmedi" durumlarını ayırt edemiyordu — bayat bir önbelleklenmiş zincir, uygulamanın az sonra kullanmayı bırakacağı bir anahtar kümesi için safe-to-clear raporlayabiliyordu. Çözdüğü anahtar zinciri artık .env/süreç ortamıyla eşleşmeyen önbelleklenmiş bir config, sonucu incomplete'e (exit 1) düşürüyor ve php artisan config:clear çalıştırın talimatı veriyor — bu, bir rotasyonu kalıcı veri kaybına çevirebilecek bir yolu kapatıyor. Bkz. encryption.tr.md.

  • sk:install'in mevcut-uygulama tespit işaretleri production'da ölüydü. EXISTING_APP_DIRECTORY_MARKERS, stub ağacının hiç göndermediği resources/js/Pages/Admin (büyük harfli Pages) taşıyordu — yalnızca case-insensitive yerel dosya sistemleri (macOS) bu uyumsuzluğu maskeliyordu. KIT_SCHEMA_TABLES ise bu kit'in hiçbir zaman oluşturmadığı bir file_manager_folders tablosunu kontrol ediyordu; gerçek tablo file_folders. İkisi de düzeltildi, bu işaretlerin varlık amacı olan fail-closed tespit adımı geri geldi.

  • Kurulum sırasında ulaşılamayan bir veritabanı artık başarılı bir kurulum raporlamıyor. sk:install, bir bağlantı hatasında veritabanı bloğunu (migration, seeder, izin tohumlama) yalnızca ekrandaki bir uyarıyla atlıyor, yine de stub-hash registry'sini yazıyor, resume checkpoint'ini temizliyor ve 0 ile çıkıyordu. Koşu artık eksik olarak bitiyor: registry yazılmıyor, checkpoint korunuyor (böylece --resume tam kaldığı yerden devam ediyor) ve komut sıfırdan farklı bir çıkış koduyla çıkıyor. Bkz. UPGRADE.tr.md.

  • sk:install artık yeniden kurulumda ya da --resume koşusunda package-lock.json'ı silmiyor. Lockfile, uygulamanın sabitlenmiş bağımlılık grafiğidir; installFrontend() ise onu npm install öncesinde koşulsuz siliyordu — yani yeniden çalıştırma, koşunun kendi özeti dosyayı "korundu" diye raporlarken bile her paketi uygulamanın hiç test edilmediği sürümlere yeniden çözüyordu. Silme artık yalnızca ilk kurulumda yapılıyor (ilgisiz bir package.json'dan kalan lock orada yalnızca engel olur) ve karar Installing npm dependencies adımının içine taşındı: checkpoint'lenmiş bu adımı atlayan bir --resume koşusu, ilk koşunun az önce yazdığı lockfile'ı artık silmiyor — silseydi onu yeniden üretecek hiçbir şey kalmıyordu.

  • --resume koşusu artık az önce kurduğu node_modules'ı silmiyor. Eski bağımlılık ağacını temizleme işi Installing npm dependencies adımının önünde duruyordu ve yalnızca ortada bir ağaç varsa çalışıyordu — yani ilk koşuda temizlenecek bir şey, dolayısıyla checkpoint'lenecek bir adım yoktu; oysa ağacı OLUŞTURAN npm install checkpoint'leniyordu. Yarıda kalan bir kurulumun ardından sk:install --resume, yeni kurulmuş node_modules'ı siliyor, sonra onu yeniden dolduracak kurulumu atlıyor ve npm run build'i eksik bağımlılıklarla baş başa bırakıyordu. Temizlik artık aynı adımın içinde, lockfile kararının yanında: adım atlanırsa ikisi de atlanıyor.

  • encryption:key'in "Next steps" çıktısı artık belgelenen rotasyon sırasıyla uyuşuyor. Numaralı liste encryption:rekey ile açılıyordu; oysa önbelleklenmiş bir config hâlâ az önce emekliye ayrılan anahtarı çözer — bu yüzden çıktıyı izleyen bir operatör her satırı yanlış anahtara yeniden şifreliyor ya da hiçbir şey yazmıyordu. Config önbellekliyse php artisan config:clear artık rekey'den önce kendi başına birinci adım olarak listeleniyor; tam olarak Veri Şifreleme belgesindeki sıra.

  • encryption:key'in .env okumaları artık çalışan uygulamanın gerçekten çözdüğü değerle eşleşiyor. DATA_ENCRYPTION_KEY, APP_KEY ve DATA_ENCRYPTION_PREVIOUS_KEYS, .env'den elle yazılmış bir regex ile okunuyordu; bu regex ${APP_KEY} gibi enterpolasyonlu bir atamayı çözmeden olduğu gibi döndürüyor, satır-içi yorumlar ve bazı tırnak durumlarında gerçek dotenv ayrıştırıcısından farklı davranıyordu. Okumalar artık uygulamanın önyüklendiği aynı ayrıştırıcıdan (Dotenv::parse()) geçiyor; yani enterpolasyonlu bir referans aynı şekilde çözülüyor ve DATA_ENCRYPTION_PREVIOUS_KEYS'e eklenen değer referans değil çözülmüş materyal oluyor. Ayrıştırıcının okuyamadığı bir .env artık — hiçbir anahtar üretilmeden veya hiçbir şey yazılmadan önce — rotasyonu durduruyor; ayrıştırıcının kendi hata mesajı raporda gösterilmiyor, çünkü bozuk satırı olduğu gibi alıntılayabilir ve bu satır anahtar materyali olabilir. Rotasyon ayrıca süreç ortamı bu anahtarlardan birini geçersiz kıldığında da duruyor — doğrudan ya da enterpolasyonlu bir değerin referans verdiği bir değişken üzerinden — çünkü o durumda dosya bir değeri söylerken çalışan uygulama başkasını çözer; .env'i yeniden yazmak bu farkı kapatamaz, süreçteki değer kazanmaya devam eder. Tek güvenli hamle, hiçbir anahtar üretilmeden durup ayrışmayı operatöre bırakmaktır.

  • media tablosu geri alındığında şema ile kayıt defteri artık ayrışmıyor. create_media_table'ın bir down()'ı yoktu ve Laravel'in migrator'ı bu çağrıyı method_exists ile koruyor — yani php artisan migrate:rollback tabloyu sessizce atlıyor, ama migration'ın kayıt defteri satırını yine de siliyordu. Tablo ayakta kalıyor, kaydı kalmıyor ve bir sonraki migrate zaten var olan bir tabloda patlıyordu. Artık yıkmak yerine reddeden bir down() tanımlıyor: boş tablo düşürülüyor, içinde satır varken denenen bir rollback ise hatayla duruyor. Dolu bir media'yı düşürmek satırları kaldırırdı, dosyaları değil — Spatie alttaki blob'ları yalnızca modelin deleting event'i üzerinden siler, bir şema rollback'i bunu atlar; geriye onları indeksleyecek hiçbir şey kalmadan öksüz dosyalar kalırdı. Geri almak istiyorsanız önce media'yı uygulama üzerinden silin. Aynı zincirdeki iki sonraki migration (add_folder_id_to_media_table, add_soft_deletes_to_media_table) da birebir aynı reddi taşıyor; çünkü bir batch en yeniden en eskiye doğru geri alınır: bu olmadan, create migration'ının koruması hiç devreye girmeden dolu bir tablodan folder_id ve deleted_at düşürülmüş olurdu. Bkz. UPGRADE.tr.md.

  • definitions.lang, tablonun birleşik unique indeksini InnoDB'nin anahtar-uzunluğu sınırının altında tutmak için daraltılıyor. Üç varsayılan 255 karakterlik sütun üzerindeki unique(['key', 'value', 'lang']), 3072 byte'lık sınırın 3060'ında duruyordu — herhangi bir sütunun tek bir karakter genişlemesi bu sınırı kırardı. lang, kitin herhangi bir yerde kabul ettiği en geniş locale değeri olan content_languages.code'a (35) daraltılıyor, böylece kitin kendi ekranlarından saklanabilen hiçbir değer etkilenmiyor; key ve value yayımlanmış 255 genişliğini koruyor, çünkü tek başına lang ~892 byte boşluk bırakıyor ve onları daraltmak yalnızca mevcut şemanın kabul ettiği veriyi bloke ederdi; yeni migration, şemaya dokunmadan önce soft-delete edilenler dahil her mevcut satırı ölçüyor ve tek bir satır bile kırpılacaksa — şemayı değiştirmeden — reddediyor. Her iki yön de unique indeksin var olduğunu doğrulayarak bitiyor; indeksi zaten eksik hâlde migration'a ulaşan bir tablo, garantisi olmadan "migrate edildi" diye kaydedilmek yerine indeksi yeniden kuruluyor. Reddederse ne temizlemeniz gerektiği için bkz. UPGRADE.tr.md.

  • sk:install artık yeniden kurulumda ya da güncellemede çakışan varsayılan Laravel dosyalarını silmiyor. package-lock.json, vite.config.* ve resources/js/app.js'i koşulsuz silmek, installer'ın ilk kurulum sonrası hiçbir koşuda yetkisi olmayan proje durumunu yok ediyordu. Silme artık yalnızca ilk kurulumda gerçekleşiyor; sonraki bir koşu bulduğu çakışan dosyaları silmek yerine raporlayıp dokunmadan bırakıyor.

  • MySQL/MariaDB CI dilimi artık install test suite'i yerine gerçek migration zincirini çalıştırıyor. tests/Feature/Install, migration'ları değil installer mantığını test ediyordu; bu yüzden MySQL veya MariaDB'nin daha katı DDL'inde kırılan bir migration CI'da fark edilmeden geçebiliyordu. MariaDB job'ı da mysql sürücüsü üzerinden çalışıyordu, dolayısıyla uuid sütunları hiçbir zaman MariaDB'nin native yolunu almıyordu. İki job da artık kendi sürücüsüyle (mysql / mariadb) tests/Feature/Migration'ı çalıştırıyor.

  • Ayar cache'i dış transaction commit olduktan sonra temizleniyor, sırasında değil. setValue() / setGroup() Cache::forget('settings') çağrısını satır içi yapıyordu; dış bir transaction içine sarılmış bir yazım (UpdateAuthSettingsAction) satırlar henüz commit edilmemişken snapshot'ı düşürüyordu — araya giren bir okuyucu miss alıp yazım öncesi satırları okuyabiliyor ve bir saat daha cache'liyordu. Temizlik artık DB::afterCommit() üzerinden geçiyor; açık transaction yokken yine anında çalışıyor.

  • Logo, favicon ve avatar yüklemeleri eskiyi düşürmeden önce yeniyi kaydediyor. Üçü de mevcut varlığı önce siliyordu; bu yüzden başarısız bir store() çağrısı ayarı artık var olmayan bir dosyaya işaret eder halde bırakıyordu. Başarısız bir yükleme artık mevcut görseli yerinde bırakıp hata döndürüyor.

  • Bir medya nesnesi diskten, ancak satırının silinmesi commit olduktan sonra kaldırılıyor. Spatie'nin MediaObserver::deleted() çağrısı dosyayı, satırı silen transaction'ın içinde kaldırıyordu; rollback ise çoktan gitmiş bir dosyaya işaret eden satırı geri getiriyordu. Kaldırma artık DB::afterCommit() üzerinden geçiyor: rollback'te atılıyor ve geriye kalan en kötü sonuç, kurtarılabilir bir öksüz dosya oluyor. Transaction'sız bir silme bugünkü zamanlamasını koruyor ve hatasını yine yüzeye çıkarıyor.

  • Çöpten klasör geri yükleme artık yinelenen isim oluşturmuyor. CreateFolderAction yinelenen ismi reddediyor, ancak kök seviyede çöp bunun etrafından dolaşmanın bir yoluydu: MySQL ve SQLite iki NULL parent_id değerini ayrı sayar ve unique index devreye girmez. Geri yükleme artık aynı domain hatasıyla reddediyor.

  • FileManager kota hesabı çıplak Spatie Media modelinde yeniden çalışıyor. computeStorageUsed() koşulsuz withTrashed() çağırıyordu; SoftDeletes trait'i olmadan bu macro bulunmadığı için her upload doğrulaması BadMethodCallException fırlatıyordu. Artık trait'in geri kalanının zaten kullandığı yetenek-farkındalıklı yardımcıdan geçiyor.

  • file-manager:purge-trash artık tüm çöpü belleğe almıyor ve hataları raporluyor. Komut silmeden önce eşleşen her satırı get() ile okuyordu; artık iki scheduler'ın aynı satırları temizlemesini engelleyen bir cache kilidi alıyor, satırları chunkById ile yürüyor (--chunk=, varsayılan 500), bir öğe hata verdiğinde devam ediyor ve geride bir şey kaldıysa sıfırdan farklı bir çıkış kodu dönüyor. Yayınlanan schedule girdisi withoutOverlapping() kazandı.

  • Konsol çıktısı, kurulu bir uygulamada gerçekten var olan doküman URL'lerine yönlendiriyor. /docs/** export-ignore işaretli olduğundan prefer-dist kurulumunda docs dizini yoktur — buna rağmen sk:install, sk:update, sk:upgrade ve sk:doctor migration ve anahtar rotasyonu sırasında yerel docs/… yolları basıyordu. Artık kurulu sürüme sabitlenmiş bir URL basılıyor.

  • encryption:health ve encryption:rekey hangi yüzeyler için gerçekten kefil olabildiklerini söylüyor. İkisi de kit'in kendi anahtar zincirini raporluyordu; Fortify ya da model-cast yolunda consumer'ın kurduğu bir encrypter onlara görünmüyordu, dolayısıyla bir rekey hiç yeniden şifrelemediği satırlar için başarı raporlayabiliyordu. Artık her yüzey, kendisine hizmet eden encrypter'la birlikte raporlanıyor; kit'in kurmadığı bir encrypter kefil olunamayan olarak adlandırılıyor (verdict: not-covered, çıkış 1) ve encryption:rekey tam bir rekey basmak yerine tek satır okumadan reddediyor. Bayat yayınlanmış config açığı — encryption bloğundan önce yayınlanmış bir config/starter-kit.php, ki orada DATA_ENCRYPTION_KEY etkisizken health "temizlemesi güvenli" diyordu — kendi ayrı teşhisi olarak raporlanıyor.

  • Şifreleme anahtarı, izinleri kısıtlanamayan bir dosyaya asla yazılmıyor. encryption:key geçici dosyasını 0600 yapıyor ama sonucu kontrol etmiyordu; izinleri yok sayan bir dosya sisteminde bir sonraki satır anahtar materyalini herkesçe okunabilir bir dosyaya yazıyordu. Mod artık dosya hâlâ boşken doğrulanıyor, sağlanmıyorsa komut duruyor.

  • encryption:key ve encryption:rekey aynı anda çalışamıyor. İkisi de anahtar zincirini okuyup yenisine karar veriyor ve geri yazıyor; eşzamanlı iki çalışma mevcut satırları okumak için hâlâ gereken bir anahtarı listeden düşürebiliyordu. Artık tek bir cache kilidini paylaşıyorlar; --dry-run rekey etkilenmiyor.

  • FileManager dosya DELETE isteği context'ini diğer route'lar gibi doğruluyor. Endpoint context DTO'sunu doğrudan request'ten kuruyordu; bozuk bir context belgelenen 422 zarfı yerine 500 olarak yüzeye çıkıyordu.

  • Kullanıcı oluşturma/düzenleme dialog'undaki saat dilimi seçimi artık yalnızca site varsayılanını değil, tüm saat dilimlerini listeliyor. UserForm tanımlayıcı listesini prop olarak alıyor; her iki dialog'un açıldığı Admin/Users/Index sayfası ise bunu hiç geçmiyordu, dolayısıyla bileşen boş varsayılanına düşüyor ve seçim kutusunda tek bir "site varsayılanı" seçeneği kalıyordu. Liste artık UserController::index() tarafından veriliyor ve iki dialog.open() çağrısında da iletiliyor. Tam sayfa Users/Create ve Users/Edit route'ları bundan etkilenmiyordu.

  • Bir FB.datePicker() değeri artık form round-trip'inde bir gün kaymıyor. Sunucudan gelen yalnızca-tarih bir string ("2024-03-10"), JavaScript'in UTC gece yarısı saydığı new Date(value) ile parse ediliyordu; submit için geri formatlarken (toLocalDateStr) tarayıcının yerel saat diliminde okunuyordu — bu da UTC'nin gerisindeki her saat diliminde günü kaydırıyordu. Tarih artık geri serileştirilme şekliyle eşleşecek şekilde bileşen bazında (new Date(year, month - 1, day)) yerel gece yarısı olarak parse ediliyor.

  • SkForm artık aynı başarısız istek için iki hata toast'ı göstermiyor. Form, kendi spesifik metniyle data_load_error / options_load_error toast'larını yükseltiyordu, ama internal useApi() çağrısı da aynı hata için composable'ın varsayılan genel toast'ını tetikliyordu. SkForm'un useApi() örneği artık { toast: false } ile opt-out ediyor; consumer'ın kendi useApi() çağrıları etkilenmiyor.

  • FB.checkboxGroup().optionsUrl(...) artık gerçekten uzak seçenekleri çekiyor. Dinamik seçenek watcher'ı, select-benzeri field'ları checkbox-group'u içermeyen hardcoded bir tip listesiyle eşleştiriyordu; bu yüzden optionsUrl ile konfigüre edilmiş bir checkbox-group field'ı sessizce hiç veri çekmiyordu — artık her yerdeki aynı SELECT_TYPES setini kullanıyor.

  • Bağımlı bir optionsUrl field'ından gelen bayat bir yanıt artık daha yeni bir yanıtın üzerine yazamıyor. Bağımlı bir select'in URL'sini süren field'ı hızlıca değiştirmek (bir arama kutusuna yazmak, hızlı yeniden seçimler) daha eski bir isteğin yanıtının daha yeni birinden sonra gelmesine izin verebiliyordu; bu da eski seçenekleri gösteriyordu. Her field artık monoton bir istek-başına sayaç tutuyor; bir yanıt yalnızca o field için hâlâ en son istekse uygulanıyor, aksi halde sessizce düşürülüyor (seçenek yazımı yok, hata toast'ı yok).

  • Salt-okunur işaretlenmiş bir form/field artık field'ın kendi .props({ disabled: ... })'u ile tekrar aktive edilemiyor. Form-level disabled (.permission()'dan) ya da field'ın kendi hesaplanan disabled'ı artık bir varsayılan değil bir taban değeri — .props({ disabled: false }) artık salt-okunur bir formu kilidini açamıyor, .props({ disabled: true }) ise başka türlü etkin bir field'ı hâlâ disable ediyor.

  • Create formundaki çevrilebilir field varsayılanları artık field'ın gerçekten render ettiği locale'lerle eşleşiyor. Boş { locale: '' } tohumu, admin-UI locale'lerini listeleyen availableLocales'i doğrudan Inertia sayfasından okuyordu; field'ın kendisi ise DB-destekli içerik locale'lerini TranslatableInput üzerinden render ediyor. İki liste farklılaştığında, gönderilen payload field'ın hiç göstermediği locale'ler için key taşıyabiliyordu (ya da gösterdiklerini eksik bırakabiliyordu). İkisi de artık aynı core/locales helper'ları üzerinden çözülüyor.

  • maxFileSize veya çoklu-dosya fileLimit'i aşan bırakılmış bir dosya artık sessizce eklenmek yerine reddediliyor. FB.fileUpload()'ın sürükle-bırak yolu artık dosya seçiciyle aynı doğrulamadan geçiyor, keep-list'te zaten olan bir dosyayı dedupe ediyor ve kaldırıldığında blob object URL'ini sızdırmak yerine iptal ediyor.

  • Altı wrapper-render edilen field tipinde (input-number, date-picker, select, multiselect, toggle-switch ve .feedback() açıkken password) bir <label for> artık odaklanabilir bir elemanı hedefliyor. Bu tipler PrimeVue kontrolünü odaklanamayan bir wrapper içinde render ediyordu; dolayısıyla düz bir label[for=key] tıklanabilir hiçbir şeyi işaret etmiyordu — iç kontrol artık PrimeVue'nun inputId'si üzerinden ${key}__control alıyor ve label'ın for'u bu id'yi hedefliyor.

  • Dikey SkTabs sekme butonları artık kapsayan bir formu submit etmiyor. Sidebar nav <button>'ının açık bir type'ı yoktu, bu yüzden tarayıcılar onu type="submit" sayıyordu — bir <form> içindeki bir sekmeye tıklamak, yalnızca sekme değiştirmek yerine formu submit edebiliyordu. Artık type="button" olarak ayarlanıyor.

  • Mount sonrası değişen bir sekmenin visible/disabled durumu artık URL'e senkron aktif sekmeyi doğru şekilde belirliyor. useUrlTab() mount anında alınmış sabit bir sekme listesi snapshot'ını kapsıyordu; bu yüzden sonradan görünür hale gelen bir sekmeye ?tab= ile ulaşılamıyordu ve gizlenen ya da disable edilen aktif bir sekme arayüzü artık listede olmayan bir sekmede bırakıyordu. Seçilebilir liste artık canlı visible/disabled durumuyla senkron tutulan reactive bir dizi — yeni görünür hale gelen bir sekme anında seçilebilir oluyor, gizlenen veya disable edilen aktif bir sekme ise ilk seçilebilir sekmeye düşüyor.

  • Disabled bir sekme artık ?tab= üzerinden aktive edilemiyor ve disabled bir ilk sekme artık parametresiz varsayılan olmuyor. useUrlTab() artık hem URL parametresini hem de "parametre yok" varsayılanını tüm liste yerine seçilebilir (disabled olmayan) sekme listesine göre çözüyor.

  • Zaten aktif olan sekmeyi tekrar seçmek artık bir Inertia visit'i tetiklemiyor ve #hash artık sekme geçişinde korunuyor. Aktif sekmeye tekrar tıklamak (veya onu programatik olarak mevcut değerine atamak) hâlâ router.visit()'i çağırıyordu; artık no-op. Sekme değiştirmek ayrıca mevcut URL'deki #hash'i düşürmek yerine koruyor.

  • Users tablosunda sayfalar-arası toplu seçim artık datatable'ın render ettiği aynı created_at_from/created_at_to tarih aralığı sınırlarını uyguluyor. UserBulkSelectionQuery artık tarihleri, tablonun kendi sorgusunun kullandığı aynı helper olan DatatableQueryBuilder::applyCalendarDateRange() üzerinden uyguluyor; böylece toplu işlem için çözülen küme, saat dilimi/DST sınırlarında görünür kümeden artık sapamıyor.

  • Seçili bir satırın id'si, toplu işleme tablonun gösterdiği şekliyle, sayısal dönüşüm yapılmadan gönderiliyor. useDatatableSelection()'ın executeBulkAction()'ı artık sayısal görünümlü bir id'yi göndermeden önce dönüştürmüyor; UUID/ULID ve integer birincil anahtarlar aynı şekilde değişmeden geçiyor.

  • API Token ve API Client tablolarındaki ID kolonu artık yeniden sıralanabiliyor. ApiTokenController::dtApi() ve ApiClientController::dtApi() artık name ve created_at'in yanına id'yi de allow-list'e alıyor; ID başlığına tıklamak artık Spatie QueryBuilder'ın InvalidSortQuery 400 hatasını döndürmüyor.

  • BulkActionRequest, ids taşımayan sayfalar-arası bir toplu isteği artık reddetmiyor. Publish edilen request, select_all_filtered true olsa bile ids (min:1) istiyordu; bu, belgelenmiş payload'la çelişiyor ve useDatatableSelection().executeBulkAction()'ı "tümü" modunda mevcut sayfada hiçbir şey seçili değilken çağıran bir host'a 422 döndürüyordu (gönderilen Users/Roles sayfaları o duruma hiç düşmez — "tümünü seç" yalnızca toplu işlem çubuğundan sunulur). ids artık Rule::requiredIf(! select_all_filtered); gönderilen id'ler yine şekil olarak doğrulanır (array, max:500, opak string). Değiştirilmemiş bir kopya sk:update ile yenilenir.

  • Gönderilen lvntr-kit-frontend ve lvntr-starter-kit skill'leri artık gerçek otomatik etiket anahtarını söylüyor. İkisi de ajana, atlanan bir .label()'ın var olmayan bir lang/{locale}/sk-attribute.php dosyasındaki sk-attribute.attributes.{key}'den çözüldüğünü söylüyordu; FB alanları ve datatable sütun/filtre etiketleri validation.attributes.{key}'den (lang/{locale}/validation.php) çözülür. FormBuilder rehberi ayrıca harici v-model modunda initialData(), bir alanın .default() değeri ve dataUrl verisinin yalnızca dahili formu beslediğini, bağlanan nesneyi asla doldurmadığını artık belirtiyor.

  • SkTabs ikonları yardımcı teknolojilerden gizleniyor ve checked bir sekme durumunu duyuruyor. Her iki düzende de sekme ikonları aria-hidden="true" taşıyor (adı zaten label veriyor); .checked() işareti — süs değil, durum — görsel olarak gizli bir metinle (sk-common.completed, EN/TR paketlerine yeni anahtar) eşleştirildi, böylece ekran okuyucu atlamak yerine bunu okuyor.

  • Sayfalar-arası "filtrelenmiş tümünü seç" artık literal bir true / false filtre değerini tablonun uyguladığı gibi uyguluyor. Spatie'nin QueryBuilderRequest'i bu iki string'i herhangi bir datatable filtresi çalışmadan önce boolean'a çevirir; BulkFilterSnapshot::normalize() ise onları metin olarak geçiriyordu — bu yüzden bir true araması, tablo "1" ile eşleşmişken "true" kelimesiyle eşleşen bir toplu küme çözüyor ve tablonun hiç göstermediği satırlara ulaşabiliyordu. Snapshot artık aynı dönüşümü yapıyor; kelime-arama koşulu da tablonun search filtresinin ve gönderilen UserBulkSelectionQuery / RoleBulkSelectionQuery'nin ortak kullandığı tek helper olan DatatableQueryBuilder::applySearchWords()'e taşındı, böylece iki yol bir değerin nasıl bölündüğü, kaçışlandığı ya da dönüştürüldüğü konusunda artık ayrışamaz.

  • Datatable arama kutusuna yazılan virgül artık isteği bozmuyor. Spatie'nin query builder'ı her filter[...] değerini tablonun arama callback'i görmeden önce , üzerinden ayırıyor; bu yüzden Acar, Levent gibi bir arama callback'e dizi olarak ulaşıyor ve istek TypeError ile (HTTP 500) düşüyordu. DatatableQueryBuilder::applySearchWords() artık ayrılan değeri aynı ayraçla yeniden birleştirip metni yazıldığı gibi arıyor; sayfalar arası toplu seçim ham metni zaten uyguluyordu, iki taraf da aynı kümeyi çözüyor.

  • SkDatatable'ın masaüstü arama kutusundaki temizle (×) kontrolü artık klavyeden erişilip kullanılabilen gerçek bir <button>. Daha önce erişilebilir adı olmayan, yalnız tıklanabilen bir <i> ikonuydu; artık yeni sk-datatable.clear_search etiketini taşıyor ve mobil arama popover'ının temizle butonu da aynı etiketi kullanıyor (eskiden "Kapat" diye okunuyordu). Bu kontrolü <i> elemanı olarak hedefleyen bir test ya da stylesheet artık butonu hedeflemeli — sk-dt-search__clear sınıfı değişmedi.

  • TranslatableInput label'ları artık input'larıyla ilişkilendiriliyor. Dil seçicinin yanında (ve tek dil modunda) render edilen label for = alan key'ini, aktif input da eşleşen id'yi taşıyor; label'a tıklamak alanı odaklıyor — normal form alanlarının zaten kullandığı işaretlemenin aynısı. Tek locale render edildiğinde zorunlu alan ayrıca o input'u aria-required ile işaretliyor ve dekoratif yıldızı (artık aria-hidden) sr-only bir "zorunlu" metniyle eşliyor; birden çok locale varsa yıldız yalnız görsel kalıyor, çünkü HasTranslatableRules::translatableRules() sadece varsayılan locale'i zorunlu tutup diğerlerini nullable yapıyor — her dil sekmesini zorunlu diye okutmak yanlış olurdu. translatable-editor ise for yerine aria-labelledby ile adlandırılıyor: düzenlenebilir düğümü label[for]'un hedefleyemeyeceği bir contenteditable <div>.

  • EditorInput'un id ve ARIA attribute'ları artık kullanıcının gerçekten yazdığı düğümde. id <EditorContent>'in sarmalayıcı <div>'ine konuyordu, Tiptap ise contenteditable düğümü onun içine render ediyor; bu yüzden label[for] label'lanamayan bir sarmalayıcıyı gösteriyor ve yardımcı teknolojiler editör için ad, rol ya da zorunluluk durumu okuyamıyordu. Düzenlenebilir düğüm artık id, role="textbox", aria-multiline="true" ve yeni ariaLabelledby / ariaRequired prop'larını taşıyor. Editörü #<alan key> ile seçen bir stylesheet ya da test artık .sk-rte__body sarmalayıcısı yerine içteki .sk-rte__content düğümüyle eşleşir.

  • encryption:key artık mevcut bir DATA_ENCRYPTION_PREVIOUS_KEYS girdisinin anlamını değiştirmiyor. Komut yalnızca emekliye ayırdığı anahtarı normalize ediyordu; listede zaten bulunan girdiler olduğu gibi kopyalanıyordu. Liste phpdotenv üzerinden okunur (çevreleyen tırnaklar soyulur, ${VAR} çözülür) ve tırnaksız geri yazılır; bu yüzden #, $ veya boşluk içeren bir girdi bir sonraki boot'ta farklı bir anahtar olarak dönüyordu — # bir yorum açıp anahtarı kısaltıyor. Artık her girdi, ham hâli bir .env satırında hayatta kalamıyorsa env-güvenli hâle getiriliyor (aynı baytlara çözülen base64: biçiminde yeniden yazılıyor).

  • encryption:key artık geçici .env dosyasının tam yazıldığını ve diske indiğini doğrulamadan onu yerine taşımıyor. Dolu bir disk Filesystem::put()'un hata fırlatması yerine kısa bir bayt sayısı döndürmesine yol açar; bu yüzden kırpılmış bir gövde eksiksiz bir .env'in yerine geçebiliyordu — komutun iki yazmasından ilkinde o gövde, emekliye ayrılan anahtarın tek kopyasını taşır. Başarısız bir yeniden-açma, fflush() ya da fsync() de işlemi durduruyor: bu dosyada dayanıklılık güvenlik özelliğinin ta kendisi, sessizce hiçbir şey yapmayan bir flush ise iki-yazma sırasını yeniden salt bir çağrı sırasına indirger.

  • encryption:key artık .env'in sahip ve grup bilgisini yazdığı dosyaya taşıyor; taşıyamıyorsa dosyayı değiştirmeyi reddediyor. www-data:www-data sahipli bir .env üzerinde sudo php artisan encryption:key, web kullanıcısının okuyamadığı root sahipli bir dosya bırakıyordu. Sahiplik en iyi çaba ile geri veriliyor (yalnızca root bir dosyayı başka bir kullanıcıya devredebilir) ama SONUÇ doğrulanıyor ve uyuşmazlık rename'den önce işlemi durduruyor: chown yapamayan operatör tam da dizine rename edebilen deploy kullanıcısıdır, yani uyarıp devam etmek servisin okuyamadığı bir .env gönderirken başarı raporluyordu. Mode geri yüklemesi aynı kuralla doğrulanıyor — genişlemiş bir mode anahtarı operatörün dışladığı okuyuculara sızdırır, daralmış bir mode ise onu okuması gereken servisi kilitler; ikisi de durdurur. Bu yolların hiçbirinde mevcut .env değiştirilmez.

  • Kitin dosya yazan komutları artık kitin kendi paket klasörü üzerinde çalışmayı reddediyor. Kapsam: sk:install, sk:update, sk:upgrade, sk:publish, sk:eject, make:sk-domain, remove:sk-domain, env:sync ve encryption:key. Paket vendor/bin/testbench ile geliyor; bu yüzden bir klon içinde vendor/bin/testbench sk:install çalıştırmak, temel dizini klonun içinde duran Testbench iskeleti olan gerçek bir Laravel uygulamasını ayağa kaldırıyordu — ve o iskeletin vendor/ dizini doğrudan klona sembolik bağdır. Boş iskelet üzerinde "zaten kurulu" işaretlerinin hiçbiri tetiklenmediği için çalışma tertemiz bir ilk kurulum sayılıyor ve zorla-üzerine-yaz yoluna giriyordu: stub'ları yayınlıyor, package.json'ı birleştiriyor, .env'i yeniden yazıyor, domain üretiyor ve migration'ları paket kaynakları üzerinde çalıştırıyordu. Her komut artık ilk bayt yazılmadan duruyor: yazma hedefi paket kökü ise ya da onun içinde kalıyorsa ve o dizin kiti vendor/lvntr/laravel-starter-kit üzerinden composer ile gerektirmiyorsa reddediyor. Bir tüketici uygulaması hiçbir zaman klonun içinde olmaz; klonun altında duran ama kiti gerçekten gerektiren bir deneme uygulaması (type: path deposu, sembolik bağ dahil) normal şekilde kurulmaya devam eder. Guard yalnızca yazma ile ilgili: --dry-run (sk:install, sk:update, sk:eject), encryption:key --show ve klon dışını gösteren bir --destination= (sk:publish, sk:eject) geçer — henüz var olmayan bir hedef, mevcut en yakın üst dizinine göre sınıflandırılır, dolayısıyla klon içinde henüz oluşturulmamış bir dizin adı vermek bir atlatma yolu değildir. --force de bunu aşmaz: anlamı "adını verdiğim dosyaların üzerine yaz", asla "kiti kitin içine kur" değildir. Paketin kendi Pest test paketi muaftır; çünkü bu komutları iskelet üzerinde bilerek çalıştırır.

  • sk:doctor'ın FileManager disk kontrolü artık FileManager'ın gerçekten yazdığı diski takip ediyor. Kontrol daha önce config/file-manager.php'nin hiç tanımlamadığı bir file-manager.disk config anahtarını okuyordu; bu yüzden fallback (filesystems.default) kontrolün gerçekte baktığı tek şeydi — Storage'ı uygulamanın varsayılanından farklı bir diske (media-library.disk_name, SettingsServiceProvider tarafından set edilir) yönlendiren bir kurulum, FileManager'ın hiç kullanmadığı bir diski sessizce doğrulatıyordu. Artık önce media-library.disk_name'i okuyor, bu anahtar yoksa yalnızca filesystems.default'a düşüyor. filesystems.disks altında hiç tanımlı olmayan bir disk adı artık FAIL veriyor (öncekinde disk "configured" ve OK olarak raporlanıyordu); kökü var olan ama web sunucusu kullanıcısı tarafından yazılabilir olmayan bir local/public disk artık OK yerine WARN veriyor.

  • Yeni files.preview route'u artık HTTP Range isteklerini yanıtlıyor. FilesystemAdapter::response() bir StreamedResponse döndürüyor; bu da Range başlığını tümüyle yok sayıp gövdenin tamamını 200 ile veriyordu — FileItemDTO bu route'a yönelmeye başlayınca FilePreviewModal içindeki satır içi oynatıcıda ileri/geri sarma çalışmaz oldu, Safari ise oynatmayı hiç başlatmadı. local sürücülü disklerde her iki servis eylemi de artık BinaryFileResponse döndürüyor; Accept-Ranges/Content-Range/206 işini prepare() üstleniyor. Gerçek bir dosya sistemi yolu olmayan uzak sürücüler akış yanıtını korur.

  • Editörden eklenen görseller yeniden yayımlanabiliyor. EditorInput.vue, yüklenen (veya seçilen) dosyanın URL'sini doğrudan kalıcı içeriğe <img src> olarak yazıyor ve bu içerik hiç kimlik doğrulamayan ziyaretçilere gösteriliyor — dolayısıyla oturuma bağlı önizleme route'una işaret etmek yayımlanmış tüm görselleri bozdu. FileItemDTO artık ikinci bir alan taşıyor: public_url — disk 'visibility' => 'public' ilan edilmişse Media::getUrl(), aksi hâlde null. Editör public_url gömüyor, yalnızca yoksa oturuma bağlı url'ye düşüyor. Bu ek bir açığa çıkarma değil: public disk o baytları, API ne döndürürse döndürsün, yolu bilen herkese sunar; dosya tarayıcısı ise her yerde oturuma bağlı url'yi kullanmayı sürdürür. Private diskte public_url tasarım gereği null'dır — yayımlanacak bir bağlantı yoktur.

  • sk:install artık zaten kurulu bir uygulamanın üzerine sessizce yeniden yayımlamıyor. Yarım kalan önceki bir çalışmadan kalan install-progress.json checkpoint'i — örneğin veritabanına ulaşılamadığı bir çalışmadan — --resume verilip verilmediğine bakmaksızın hem existing-app-markers guard'ını hem de onay istemini her sonraki düz yeniden çalıştırmada devre dışı bırakıyordu; stepAlreadyCompleted() bir adımı gerçekten atlamak için zaten açık bir --resume istiyordu, yani aslında hiçbir şey "resume" edilmiyordu, sadece guard'sız kalıyordu. Yeni bir confirmReinstall() guard'ı artık hash registry'si (storage/starter-kit/hashes.json) zaten mevcutken ve çalışma gerçek bir --resume değilken yeniden yayımlamadan önce soruyor; --no-interaction altında --force verilmediği sürece kapalı başarısız oluyor ve tek başına eski bir checkpoint artık iki guard'ı da bastıramıyor.

  • İki noktalı bir özel izin adı (örn. system.health.view) artık yanlış noktadan bölünmüyor. GroupedPermissionsQuery ve _01_RolePermissionSeeder bir izin adını kaynak/yetenek çiftine ayırmak için İLK noktayı kullanıyordu; çoğu izin resource.ability (tek nokta) şeklinde olduğundan sorun çıkarmıyordu, ama iki noktalı özel bir izinde kaynak system, yetenek ise health.view gibi yanlış bölünüyordu — oysa kaynak system.health, yetenek yalnızca view olmalı. İkisi de artık SON noktadan bölüyor. Bu arada permission-resources.php ve EN/TR sk-role dil dosyalarına eksik developer/api kaynak grupları ile files, api-clients, api-tokens, dashboard, system.health kaynak etiketleri ve view yetenek etiketi eklendi.

  • Roller formu artık pasif bir sekmedeki doğrulama hatasını gizlemiyor. Dikey SkTabs yalnızca aktif paneli mount ediyor; Yeni Rol ekranında varsayılan aktif sekme "İzinler" olduğundan, name/display_name/color gibi Temel Bilgiler sekmesindeki zorunlu bir alan boş bırakılıp gönderildiğinde hata hiçbir yerde görünmüyordu. Form artık başarısız bir gönderimde hatalı alanı taşıyan sekmeye otomatik geçiyor.

  • Yeni bir kurulumda artık başarılı login 404'e düşmüyor. FortifyServiceProvider, Fortify'ın kendi post-login redirect hedefi olan config('fortify.home')'u (/home) hiç ezmiyordu — bu kit o route'u hiç kaydetmiyor, gerçek iniş sayfası dashboard.index (/dashboard). Yayınlanan provider artık boot() içinde config(['fortify.home' => '/dashboard']) set ediyor; kendi kopyasını özelleştirmiş mevcut bir kurulum yeniden yayımlanana kadar etkilenmez.

2026-08-25 — v13.6.16

Düzeltildi

  • Bir datatable, sayfa URL'inde kalan komşu tablonun sıralaması yüzünden artık boş açılmıyor. sort sayfa genelinde tek bir query parametresi olduğu için, birden fazla tablo barındıran bir sayfada (sekmeler, yan yana paneller) ikinci mount olan tablo URL'deki ilk tablonun sort değerini okuyup kendi endpoint'inden o endpoint'in hiç izin vermediği bir kolona göre sıralama istiyordu — Spatie\QueryBuilder buna HTTP 400 (InvalidSortQuery) ile yanıt veriyor ve tablo boş geliyordu. Bir kolon yeniden adlandırıldıktan veya kaldırıldıktan sonra yer imine alınmış bir bağlantı da aynı şekilde kırılıyordu. SkDatatable artık geri yüklenen sıralama anahtarını kullanmadan önce kendi kolonlarıyla doğruluyor — columns içinde görünmediği hâlde sıralanabilir olduğu için id kolonu dâhil, ayrıca bu route'a kayıtlı kolon sırası da dâhil; böylece yalnızca sunucunun yayınladığı bir kolon (örneğin gizli updated_at), kullanıcı onu açtıktan sonra sıralamasını geri yüklemeye devam ediyor. Yabancı bir sıralama taşıyan URL başka bir tablonun URL'i sayılıp bütünüyle yok sayılıyor — page, per_page ve filtreler dâhil; çünkü yarısını okumak bu tabloyu yalnızca komşusunun sayfa numarasında açardı. Route bazlı session kaydından dönen eskimiş bir anahtar da aynı şekilde düşürülüyor; tablonun gerçekten kendi olan sıralaması ise her iki kaynaktan da geri yüklenmeye devam ediyor.

2026-08-24 — v13.6.15

Değişti

  • Varsayılan panel artık işlevsiz kontroller içermiyor. Yeni bir kurulumun header'ında arkasında hiçbir command olmayan dört kullanıcı menüsü girdisi (hesap ayarları, bildirim tercihleri, parola değiştir, yardım) ile uydurma sipariş, ödeme ve kişilerle doldurulmuş bildirim ve mesaj popover'ları vardı — iki dilli bir kitte sabit Türkçe metinler, sürekli yanan bir rozet ve hiçbir şey yapmayan bir "tümünü okundu işaretle" aksiyonu. Hepsi kaldırıldı; profil bağlantısı, dil alt menüsü, çıkış, görünüm popover'ı ve system_admin'e özel geliştirici popover'ı yerinde kaldı. Dashboard'daki aynı ölçüde işlevsiz Export, New Report, View All ve View Report butonları da kaldırıldı ve demo dashboard artık metriklerinin örnek içerik olduğunu belirten çevrilmiş bir bilgi bandıyla (sk-common.demo_banner) açılıyor. Grafikler, KPI kartları ve tablolar olduğu gibi duruyor: ekran kitin bileşenlerinin neler yapabildiğini göstermeye devam ediyor, yalnızca uydurma iş verisini tüketicinin kendi verisiymiş gibi sunmuyor. Kaldırılan menü girdilerinin çeviri anahtarları dil dosyalarında bırakıldı.
  • Frontend lint kapısı artık yalnızca çalışmıyor, gerçekten uyguluyor. npm run lint 33 dosyada 2.708 uyarı bildirdiği hâlde 0 ile çıkıyordu (2.473 vue/html-indent, 231 vue/max-attributes-per-line, dört başka şablon biçimlendirme bulgusu); dolayısıyla CI'ın lint adımı hiçbir zaman kırılamıyor ve gerçekten yeni bir uyarı bu gürültünün içinde görünmez kalıyordu. Tüm baseline eslint --fix ile mekanik olarak düzeltildi — yalnızca biçimlendirme, davranışa dokunulmadı. Kapı, kitin kendi CI'ının koştuğu yeni lint:ci script'i (--max-warnings=0) üzerinden uygulanıyor; tüketiciye giden npm run lint ise uyarıları bildirmeye devam ediyor ama başarısız olmuyor — böylece kurulu bir uygulamanın kendi kodundaki uyarılar onun lint adımını ve pipeline'ını kırmıyor.
  • Inertia sayfaları lazy çözümleniyor; ilk ziyaret artık tüm paneli indirmiyor. resources/js/app.ts içindeki iki sayfa glob'u da eager'dı; bu, login formunda bekleyen bir ziyaretçi için bile 54 Vue sayfasının tamamını — dosya yöneticisi, Tiptap editörü, tüm ayar ekranları — initial bundle'a koyuyordu; vite.config.ts içindeki catch-all vendor chunk'ı da bu ağır feature bağımlılıklarını aynı yüke sabitliyordu. Glob'lar artık lazy ve modül kapsamına taşındı, resolve eşleşen loader'ın promise'ini döndürüyor (Inertia v3 async resolver'ı hem client'ta hem SSR'da await eder; dolayısıyla eski "SSR sync resolver ister" varsayımı geçerli değil) ve catch-all chunk kaldırıldı, böylece tek sayfada kullanılan bağımlılıklar kendi dinamik import sınırlarına düşüyor. Ölçülen initial payload 652,2 kB'den 390,4 kB gzip'e iniyor (%40 azalma); çıktı 121 chunk'a bölünüyor ve entry'den 54 dinamik import ayrılıyor. Sayfa çözümünde app'in vendor'ı geçmesi, Page not found hatası ve dil glob'ları (eager kalır) değişmedi. scripts/ci/check-bundle-budget.mjs artık entry'nin statik import kapanışını gzip'leyip 500 kB üzerinde CI'ı kırıyor; böylece bir regresyon yayınlanmadan yakalanıyor.
  • Dağıtılan iskeletten iki eskimiş kalıntı kaldırıldı. app.blade.php içindeki <title> yedeği Starter Kit 12 yazıyordu — her sürümde eskiyen ve yalnızca app.name boşken ortaya çıkan bir sürüm numarası; yedek artık sürüm taşımıyor, sadece Starter Kit. Ayrıca Files sayfası MyShareLinksDrawer bileşenini import edip v-if="false" ile render ediyordu; yani backend ucunu bekleyen ~190 satırlık bir bileşen, hiçbir zaman erişilebilir olmadan Files sayfasının kendi chunk'ına giriyor ve sayfayı açan herkes tarafından indiriliyordu. Bu import ve ona ait ölü state (sessionLinks, drawerVisible, drawerMediaId, onShareRevoked) kaldırıldı; Files sayfası artık onu taşımıyor. Bileşen dosyası yerinde duruyor — özyinelemeli vendor sayfa glob'u onu hâlâ görüyor ve build hâlâ (artık hiç çekilmeyen) bir chunk üretiyor — ve GET /file-manager/share?media_id=X ucu geldiğinde nelerin yeniden bağlanacağı bir yorumla kayıt altına alındı.

Düzeltildi

  • Kurulum dokümanları artık yeni projeleri çok eski bir sürüme yönlendirmiyor. Belgelenen akış paketi :^13.0 ile ekliyordu; bu aralık, spatie/laravel-activitylog:^4.9 kabul eden son sürüm olan v13.0.1'i de kapsayacak kadar geniş. laravel/laravel iskeleti yalnızca PHP 8.3 isterken bu kit (ve activitylog:^5.0) PHP 8.4 istediği için composer create-project PHP 8.3'te başarıyla tamamlanıyor, ardından Composer platform uyuşmazlığını bildirmek yerine sessizce v13.0.1'e iniyordu; sonrasında composer update de haklı olarak "nothing to update" diyordu. README ve kurulum kılavuzları artık :^13.6 kullanıyor — böylece Composer gerçek sebebi hata olarak veriyor — ve PHP 8.4 tabanını açıkça belirtip beklenmedik bir sürüm çözümlendiğinde composer why-not lvntr/laravel-starter-kit 13.6.14 komutunu gösteriyor. Türkçe README ile sk:upgrade komutunun yönlendirme satırı bu taramada atlanmış ve hâlâ :^13.0 yazıyordu; ikisi de artık aynı constraint'i kullanıyor ve Türkçe README İngilizce olanla aynı PHP 8.4 / Node 20.19+ uyarısını taşıyor.
  • Production CSP'si artık bulut depolamadan gelen önizlemeleri bloklamıyor. img-src yalnızca 'self' data: blob: kabul ederken kit local, s3 ve DigitalOcean Spaces disklerini destekliyor ve FileManager tarayıcıya bucket'ın kendi origin'indeki imzalı URL'leri veriyordu; yani uzak bir diskte her önizleme ve frontend'in çektiği her indirme, kitin kendi koyduğu politika tarafından bloklanıyordu. SecurityHeaders artık media-library disk'i ile public disk'in origin'lerini türetiyor (CDN tabanı gibi bir disk url'i, bir s3 endpoint'i ve onun *.host bucket-subdomain biçimi, ya da düz AWS için region/bucket ikilisi) ve bunları img-src, yeni media-src ve connect-src direktiflerine ekliyor. Ek origin'ler — örneğin karşılama mesajına gömülen uzak bir görsel — yalnızca http(s) origin kabul eden starter-kit.security.csp_extra_origins anahtarına yazılıyor. Hazır bir CSP taşıyan response'a hâlâ dokunulmuyor ve local ortamı hâlâ hiç politika almıyor.
  • sk:doctor log kontrolleri config cache'li uygulamada gerçek ayarı raporluyor. LogChannelCheck ve LogStackCheck doğrudan env() okuyordu; config:cache çalıştıktan sonra .env yüklenmediği için ikisi de uygulamanın gerçekten kullandığı değeri değil kendi varsayılanlarını bildiriyordu — üstelik doctor çalıştırmanın en çok önem taşıdığı dağıtımlarda. Artık logging.default ve logging.channels.stack.channels okunuyor. LogStackCheck ayrıca yalnızca gerçekten aktif olan kanalı yargılıyor: logging.channels.stack koşulsuz okunduğu için LOG_CHANNEL=daily kullanan bir uygulama, log kayıtlarının hiç uğramadığı bir stack yüzünden uyarı alıyordu; buna karşılık gerçekten rotasyonsuz olan LOG_CHANNEL=single OK olarak geçiyordu. Kontrol artık logging.default değerini çözüyor, bir stack ise üye kanallarına açıyor ve çözülen kanallardan herhangi biri single sürücüsünü kullanıyorsa uyarıyor — hatalı kanala gerçekten ulaşan ayarı gösteriyor: aktif kanal framework'ün kendi stack'i ise LOG_STACK, başka adlı bir stack ise logging.channels.<ad>.channels, diğer durumlarda LOG_CHANNEL. Bu açılım LogManager::createStackDriver()'ı yaklaşık taklit etmek yerine birebir izliyor: string olarak yazılmış bir channels değeri (LOG_STACK=single,daily) tek bir kanal adı gibi okunmak yerine virgülden bölünüyor ve üyeler özyinelemeli çözülüyor; böylece bir stack daha derinde duran single artık rotasyonlu sanılmıyor. Döngüsel bir yapılandırma, özyinelemeye girmek yerine çözüm yolunda sonlanıyor.
  • Cache'lenmiş config artık Inertia SSR'ı sessizce kapatmıyor. Tüketici config/inertia.php dosyasını publish etmediğinde service provider her boot'ta inertia.ssr.enabled değerini env('INERTIA_SSR_ENABLED') ile ayarlıyordu. config:cache bu override'ı .env hâlâ yüklüyken doğru şekilde yakalıyor, ancak aynı kod cache'li her istekte tekrar çalışıp env() null döndüğü için cache'e yazılmış true değerini false ile eziyordu. Config cache'liyken bu override artık atlanıyor. Mevcut kurulumlar için not: INERTIA_SSR_ENABLED=true yazıp config:cache çalıştırmış bir uygulama aslında hâlâ client-side render ediyordu; bu düzeltmeden sonra SSR gerçekten devreye giriyor — php artisan inertia:start-ssr süreçinin çalıştığından emin olun. Çalışmıyorsa Inertia hata vermek yerine client-side render'a düşer (HttpGateway::dispatch(), bundle yoksa ya da bağlantı başarısızsa null döner; inertia.ssr.throw_on_error açık değilse).
  • HTTPS asset URL'i artık protokol-relative hâle getirilmiyor. SettingsController ve SettingsDefaultsQuery içindeki mixed-content koruması public disk URL'lerinin şemasını hem http:// hem https:// için siliyordu; bu yüzden HTTP üzerinden açılan bir sayfada https:// olan bir asset HTTP'ye düşüyordu — koddaki "never a downgrade" yorumunun tam tersi. Artık yalnızca http:// URL'leri yeniden yazılıyor; https:// bir URL hiçbir sayfada mixed content olmadığı için olduğu gibi geçiyor.

Güvenlik

  • Kitin kendi route'larından yirmi beşi artık ihmal yüzünden izinsiz kalmıyor. CheckResourcePermission bir izni route adından türetir; adı olmayan, adı iki segmentten az olan ya da action segmenti middleware'in ability haritasında bulunmayan bir route hiçbir izne çözülmez — daha önce bu, isteğin tamamen sessizce geçmesi anlamına geliyordu. Paketin kendi kaydettiği yirmi beş route bu boşluğa düşüyordu: beş settings.contentLanguages.* ucu, on beş settings.update.* / settings.upload.* / settings.delete.* yazma ucu, settings.testMail, roles.syncPermissions ve roles.bulk / users.bulk. Bunlar artık paketin içinde yaşayan bir route-adı sözleşmesiyle bir izne sabitleniyor; dolayısıyla mevcut bir kurulum düzeltmeyi yalnızca composer update ile alır — sk:install'in app'inize kopyaladığı route dosyalarına dokunulmaz. Her eşleme davranış-nötr olacak şekilde seçildi: settings route'ları aynı izni zaten açık bir check.permission: argümanıyla uyguluyordu, roles.syncPermissions ise kendi controller'ında ayrıca system_admin ile sınırlı. roles.bulk ve users.bulk eşlenmek yerine muaf ilan edildi: gerektirdikleri ability istek gövdesinde adı geçen aksiyona bağlı ve BulkActionDispatcher zaten her item'ı handler'ın kendi ability'siyle yetkilendiriyor; dolayısıyla route seviyesinde statik bir eşleme yalnızca fazla-reddedebilirdi (.delete, .update ve .read'in her biri farklı bir meşru rolü kırar). system-health.run da aynı şekilde adıyla muaf tutuldu, çünkü controller'ı zaten Gate::authorize('system.health.view') çağırıyor ve route grubu system_admin ile sınırlı. Ayrıca kendi açık check.permission:<izin> argümanını taşıyan bir route, parametresiz grup geçişi tarafından ikinci kez yargılanmıyor artık.

Eklendi

  • sk:doctor bir unresolved-routes kontrolü kazandı. Tüketici tarafından eklenenler dahil, CheckResourcePermission'ın izin türetemediği her route'u listeler — bunlar şu an çözülmüş bir izin yerine loglanmış bir uyarıyla geçen route'ların ta kendisi. Görmek için php artisan sk:doctor --only=unresolved-routes çalıştırın; her biri bir <resource>.<action> route adı, açık bir check.permission:<permission> argümanı ya da yeni starter-kit.permissions.unrestricted_routes config anahtarı altında bir listeleme ile düzeltilir.

  • starter-kit.permissions altında iki yeni config anahtarı. allow_unresolved (env STARTER_KIT_ALLOW_UNRESOLVED_ROUTES, varsayılan true), izni hiç türetilemeyen bir route'un loglanmış bir uyarıyla geçmesini mi yoksa reddedilmesini mi kontrol eder; mevcut allow_unmapped'in aksine, değiştirildikten sonra production'da da uygulanmaya devam eder — çünkü çözülemeyen bir route, host'a özgü bir veri boşluğu değil, route/ability-haritası arasındaki yapısal bir uyuşmazlıktır. unrestricted_routes ise bilinçli olarak izinsiz kalacak Str::is route-adı desenlerini listeler; bunlar hem kontrolden hem doctor uyarısından muaftır.

  • Yeni bir proje çözülemeyen route'larda fail-closed kuruluyor; mevcut bir projeye dokunulmuyor. sk:install, oluşturduğu .env dosyasına STARTER_KIT_ALLOW_UNRESOLVED_ROUTES=false yazıyor. Sıfırdan kurulan bir uygulamada geçmişten devralınacak route olmadığı için sıkı başlıyor ve ilk izinsiz route'u production'da değil geliştirme sırasında ortaya çıkıyor. Bu değeri hâlihazırda var olan bir uygulamaya hiçbir şey taşımıyor: ensureEnvFile() .env.example dosyasını yalnızca ilk kurulumda olduğu gibi kopyalıyor ve yeniden-kurulum yolu artık küçük bir FIRST_INSTALL_ONLY_ENV_KEYS listesini atlıyor; yani kurulu bir uygulamada sk:install'ı tekrar çalıştırmak da anahtarı eklemiyor. sk:update ve sk:upgrade zaten .env dosyasına hiç dokunmuyor. Mevcut bir kurulumun kendiliğinden reddetmeye başlayacağı bir sürüm yok — anahtarı vermeyen her uygulama için allow_unresolved varsayılanı true kalıyor; mevcut bir uygulama, sk:doctor --only=unresolved-routes temiz çıktığında satırı kendisi yazarak opt-in yapıyor. Sıralı düzeltme yolu için upgrade rehberine bakın.

2026-08-15 — v13.6.14

Güvenlik

  • Aktivite kayıtları artık kimlik bilgilerini saklamıyor. Fillable ve unguarded model loglama; password, remember_token, two_factor_secret, two_factor_recovery_codes ile *_token veya *_secret son ekli alanları hariç tutar; yalnız parola değişen bir güncelleme artık aktivite satırı oluşturmaz. Aktivite Kaydı arayüzü de eski satırlardaki bu anahtarları maskeler. Yeni geri döndürülemez veri migration'ı ve idempotent sk:redact-activity-secrets komutu bu anahtarları hem attribute_changes hem properties alanından recursive olarak kaldırır; migration öncesinde veritabanını yedekleyin ve komutun bildirdiği decode edilemeyen JSON satırlarını elle inceleyin. Modeller deny list'i sensitiveLogAttributes() ile genişletebilir. Veri migration'ı paketin içinde (database/migrations/) gelir; composer update + php artisan migrate onu sk:update olmadan taşır ve büyük/küçük harfe duyarlı bir JSON collation'da farklı yazılmış anahtar atlanmasın diye tüm satırları tarar. sk:doctor ise kimlik bilgisi taşıyan satır kaldığı sürece FAIL veren activity-log-secrets kontrolünü kazandı; bu kontrol, birincil anahtara göre ilk 500 satır üzerinde çalışan, her sürücüde aynı maliyeti taşıyan sınırlı ve salt-okunur bir sondadır ve kararı SQL'deki anahtar-adı filtresiyle değil PHP'de verir; böylece Password gibi yazılmış bir anahtar kolonun collation'ı yüzünden atlanamaz. Mesajları ölçüleni bildirir — büyük tablolarda bulgu bir alt sınır ("en az N") olarak raporlanır, temiz sonuç ise tabloyu aklamak yerine taradığı pencereyi adlandırır; tam sayım hâlâ sk:redact-activity-secrets --dry-run --all işidir (--all, SQL tarafındaki anahtar-adı ön filtresini kapatan bayraktır).
  • Kimlik doğrulama ayarları artık Fortify route'larını kaydetmeden önce fail-closed uygulanıyor. Kayıt, parola sıfırlama ve parolamı-unuttum istekleri ilgili ayar kapalıyken 403 döner. İki faktör challenge'ı, store seviyesinde atomik olan "yoksa ekle" (add-if-absent) cache girdisiyle sahiplenilir; böylece eşzamanlı iki denemeden yalnızca biri token üretebilir — Cache::pull() sahiplenme gibi görünse de ayrı bir get + forget'tir ve rate limit bu yarışı daraltır, sıraya sokmaz. Recovery code kullanımı ayrıca veritabanı satır kilidiyle korunur.
  • Kullanıcı kontrollü datatable etiketleri v-html yüzeyine ulaşmadan escape ediliyor. Rol görünen adları, aktivite kaydı causer değerleri ve API client grant type'ları artık markup enjekte edemez. Frontend dependency lock'ları axios 1.19.0, Vite 7.3.6, esbuild 0.28.2, form-data 4.0.6, shell-quote 1.9.0 ve undici 7.29.0 dahil non-breaking güncellemelerle yenilendi; hem production hem tam npm audit artık 0 güvenlik açığı raporluyor ve frontend CI npm audit --audit-level=high --omit=dev kontrolünü zorunlu tutuyor.

Eklendi

  • sk:doctor, permission-matrix kontrolünü kazandı. config/permission-resources.php kullanıcıya aittir ve sk:update bu dosyaya asla yazmaz; dolayısıyla paketin sonraki bir sürümde eklediği kaynak ve yetenekler mevcut kuruluma hiç ulaşmaz — bunun ilk belirtisi de genelde daha önce çalışan bir ekranda alınan 403'tür. Kontrol, paketin gönderdiği matris ile uygulamanın yüklü matrisini karşılaştırır, eksik olanları (files.update, files.delete vb.) raporlar ve sk:seed-permissions komutunu işaret eder. Tek yönlüdür: consumer'ın kendi eklediği kaynaklar asla raporlanmaz. Yetenekleri backing value üzerinden karşılaştırır; böylece PermissionEnum case'i olarak yazılmış bir girdi, string yazılmışla aynı sayılır. Paket tarafındaki null (tüm yetenekler) tanımını, consumer'ın genişletebildiği PermissionEnum yerine paketin kendi gönderdiği yetenek listesinden açar; uygulama tarafındaki null değerini ise her şeyi kapsıyor kabul eder.

Düzeltildi

  • sk:update artık consumer'ın genişlettiği PermissionEnum dosyasını ezmiyor. app/Enums/PermissionEnum.php paket sahiplidir ve her güncellemede yenilenir; ancak aynı zamanda public for() / allFor() yardımcılarına sahip backed bir enum olduğu için ona bir proje yeteneği eklemek (case Approve = 'approve';) yapılacak en doğal şeydir — ve şimdiye kadar bu case, dosya stub'dan sırf farklı olduğu için üzerine kopyalanıyordu: registry kontrolü yok, yedek yok, özet çıktısında tek satır yok. Kopyalama artık, diğer tüm consumer sahipli dosyaların zaten kullandığı kurulum-anı hash'i ile korunuyor: dokunulmadığı kanıtlanabilen dosya yenilenir, düzenlenmiş olan korunur ve birleştirme talimatıyla ayrıca raporlanır, hash kaydı olmayan dosya "dokunulmamış" varsayılmak yerine mevcut etkileşimli seçim ekranına düşer, --force ise yine üzerine yazar.
  • FileManager istekleri artık alt dizine kurulumlarda çalışıyor. withBasePath() idempotent hale geldi ve useApi.request() bunu merkezi olarak uyguluyor; eksik veya iki kez eklenmiş base path oluşmuyor.
  • Dashboard ve SkDatatable, SSR sırasında artık yalnız tarayıcıda bulunan global değerlere erişmiyor.

Kırılmalar

  • FileManager context yetkilendirmesi artık mutasyonları write altında birleştirmek yerine read, create, update ve delete kullanıyor. Built-in global context bunları birebir files.read, files.create, files.update ve files.delete ile eşler; bilinmeyen ability'ler fail-closed davranır. Önceden yalnız files.create sahibi bir rol silme ve çöpü boşaltma erişimini, yalnız files.update sahibi bir rol ise okuma erişimini kaybeder. Her role gereken belirli files.* yetkilerini verin, ardından php artisan sk:seed-permissions çalıştırın. Consumer context closure'ları dört yeni adı işlemeli ve artık hiçbir zaman write almayacaktır; yükseltme rehberine bakın. Doğrudan çağıranlar için authorizeWrite(), authorizeUpdate() metoduna delege eden deprecated alias olarak kalır.
  • İki faktörlü kimlik doğrulama kapatıldığında Fortify'nin 2FA route'ları artık kaldırılıyor. Kayıtlı kalmak yerine 404 dönerler. fortify-options.two-factor-authentication route kaydından önce ayarlandığı için 2FA yönetim endpoint'leri artık password.confirm middleware'ini de taşır; doğrudan API tüketicileri bu doğrulama round-trip'ini tamamlamalıdır.

2026-08-15 — v13.6.13

Değiştirildi

  • Kit artık MIT lisanslı (önceden PolyForm Noncommercial 1.0.0). Ticari kullanım kısıtsız serbest — telif ve izin bildirimi korunduğu sürece kit'i kapalı kaynaklı ve ücretli ürünlerin içinde dağıtabilirsiniz.
  • Davranış değişikliği: API Resource tarihleri artık önceden formatlanmış gösterim string'i yerine offset içeren ISO-8601 değerleri yayıyor. Böylece frontend, parse edilebilir tek bir anı kullanıcının çözümlenen saat diliminde tutarlı biçimde formatlayabilir. format_date() fonksiyonunun kendisi değişmedi; mevcut Blade, e-posta, dışa aktarma ve diğer gösterim çağrılarıyla uyumlu kalır.
  • Davranış değişikliği: saklama ve gösterim saat dilimleri ayrıldı. APP_TIMEZONE=UTC değerini koruyun; display_timezone artık APP_TIMEZONE yerine yeni APP_DISPLAY_TIMEZONE değişkenini okur. Mevcut kurulumlar değişkeni eklemeli ve config/app.php dosyasını güvenli, tekrarlanabilir config rewrite adımıyla güncellemek için php artisan sk:upgrade çalıştırmalıdır. Saklama UTC değilse sk:doctor --only=timezone-storage başarısızlık raporlar.
  • MySQL/MariaDB bağlantı oturumları artık UTC'ye sabitleniyor. sk:install ve sk:upgrade, consumer değerlerinin üzerine yazmadan veya diğer sürücülere dokunmadan config/database.php içindeki mevcut mysql/mariadb dizilerine literal 'timezone' => '+00:00' girdileri ekler. Mevcut kurulumlarda uygulama tarafından yazılmış offset'li TIMESTAMP verileri bulunabilir; Saat Dilimleri belgesindeki tek seferlik dönüşüm tamamlanana kadar bu veriler offset'li kalır. DEFAULT CURRENT_TIMESTAMP kolonları zıt yönde hareket eder ve dönüşümün dışında bırakılmalıdır. sk:upgrade, veri içeren UTC dışı oturumu değiştirmeden önce uyarır ve onay ister (--force bulunmayan etkileşimsiz çalışmalar atlar), ancak satırları hiçbir zaman dönüştürmez. sk:doctor --only=timezone-storage artık SYSTEM dahil UTC dışı MySQL/MariaDB oturumunu tespit eder; oturum okunamazsa başarılı saymak yerine uyarır.
  • Kullanıcılar kendi gösterim saat dilimini seçebilir ve datatable tarih filtreleri artık bunu dikkate alır. Boş kullanıcı tercihi “Genel site ayarını takip et” anlamına gelir ve açıkça UTC seçmekten farklıdır. Ortak kullanıcı → site → uygulama → UTC fallback'i backend ve frontend formatlamasında uygulanır; takvim-tarihi filtreleri, indeksli kolonu sarmadan DST-doğru, yarı açık UTC aralıkları kullanır.

2026-07-25 — v13.6.12

Eklendi

  • Kit'in AI skill'leri artık Claude Code'un yanı sıra Codex ile de çalışıyor. sk:install üç skill'i .claude/skills/ dizinine yayınlar ve OpenAI Codex CLI'ın native okuduğu .codex/skills/ dizinine aynalar. Özelleştirmek için .claude kopyalarını düzenleyin — .codex aynası her sk:install/sk:update'te yeniden üretilir ve o dizindeki kendi skill'lerinize asla dokunmaz. sk:install --without-ai-skill iki ağacı da atlar; sk:update --without-ai-skill tek bir çalışmada aynanın yeniden üretilmesini atlar.

Değiştirildi

  • Paketle gelen AI skill'leri güncel kit ile hizalandı (hâlâ v13.6.0 öncesi yapıyı anlatıyorlardı): vendor-first mimari, sk:eject ve kurulum sırasındaki User/Role eject'i, sk:doctor, tam sk:publish tag listesi, make:sk-domain --with= ekleri, gerçek sk:update üzerine-yazma kuralları, güncel composable'lar ve FormBuilder alan tipleri, SkForm güvenlik korumaları ve tema sistemi. Skill gövdeleri artık İngilizce (Türkçe tetikleyici anahtar kelimeler korundu) — tek skill seti iki asistana da hizmet ediyor.

2026-07-22 — v13.6.11

Düzeltildi

  • Yüklenen dosya artık iki kez görünmüyor, dosya alanı olan formlarda "kaydedilmemiş değişiklikler" uyarısı nihayet kapanıyor. Dosya yükleme alanı olan bir formu kaydettikten sonra, seçtiğiniz dosya sunucuya kaydedilmiş kopyasının yanında formda da duruyordu — aynı görsel iki satır olarak listeleniyordu. Formda kalan bu dosya aynı zamanda formu kalıcı olarak "değişmiş" gösteriyordu; bu yüzden kaç kez kaydederseniz kaydedin kaydedilmemiş-değişiklik banner'ı ve "sayfadan ayrılmak istediğinize emin misiniz?" uyarısı hiç kaybolmuyordu. Kayıt artık yüklenmiş dosya listesini sunucudan tazeleyip seçiciyi temizliyor: tek kopya görüyorsunuz, form temize geçiyor ve az önce yüklediğiniz dosya formu ikinci kez kaydettiğinizde kaybolma riski taşımıyor.

2026-07-21 — v13.6.10

Düzeltildi

  • "Kaydedilmemiş değişiklikler" uyarısı artık kayıttan sonra ekranda kalmıyor. Kendi kendine gönderim yapan formlarda kayıt başarıyla tamamlanıyordu ama form kendini hâlâ "değişmiş" sayıyordu — bu yüzden kaydedilmemiş-değişiklik banner'ı görünmeye devam ediyor, sayfayı kapatmak veya sayfadan ayrılmak her şey kaydedilmiş olmasına rağmen onay soruyordu. Kayıt artık formu anında temiz olarak işaretliyor. Kayıt sürerken yazmaya devam ederseniz, o yeni değişiklikler kaydın parçası değildi — form onlar için "kaydedilmemiş" kalmaya devam ediyor, böylece hiçbir şey sessizce kaybolmuyor. Kayıttan sonra kendini temizleyen oluşturma formları eskisi gibi temizlenmeye devam ediyor.

2026-07-08 — v13.6.9

Güvenlik

  • Seed'lenmemiş izinler artık yalnız production'da değil, staging/demo'da da reddediliyor. CheckResourcePermission middleware'i, gerekli izin veritabanında yoksa production dışındaki tüm ortamlarda (staging, uat, demo, testing) isteği geçiriyordu — böylece public bir staging veya demo host'u, izin satırı unutulmuş bir endpoint'i sessizce açığa çıkarabiliyordu. Artık local dışındaki her ortamda reddediyor (local geliştirici kolaylığı için yine uyarıp geçiriyor). Eski davranışı bilinçli olarak production dışı host'larda istiyorsanız STARTER_KIT_ALLOW_UNMAPPED_PERMISSIONS=true ayarlayın. Bkz. UPGRADE.tr.md.
  • İzin sorguları artık Octane-güvenli — seed'lenmiş izin listesi tüm worker ömrü boyunca değil, kısa süreli (60sn) cache'leniyor ve hem php artisan sk:seed-permissions hem de Roller ekranındaki izin senkronu bunu hemen temizliyor; böylece yeni seed'lenen izinler Octane altında anında etkili oluyor.

Değiştirildi

  • Datatable kolon görünürlük/sıra tercihleri sessionStorage'dan localStorage'a taşındı. Bir SkDatatable'da kolon gösterme/gizleme veya sıralama tercihiniz varsa, yükseltme sonrası bu tercih bir kez sıfırlanır — veri kaybı yok, tamamen kozmetik; tercihi yeniden ayarlamanız yeterli.

2026-07-04 — v13.6.8

Kalite ve UX sprint'i

Geniş kapsamlı bir kalite-kontrol turu: audit-log kapsamı, kurulum/yükseltme DX'i, erişilebilirlik ve bir login-throttle güvenlik düzeltmesi. sk:update gerektiren tek publish edilmiş dosya değişikliği var — bkz. UPGRADE.md.

Güvenlik

  • login_throttle = '0' artık web login rate limiter'ını tamamen devre dışı bırakmıyor — throttle'ı tamamen kaldırmak yerine bilinçli olarak gevşek bir taban limiter'a geçiyor; böylece hiçbir admin ayarı web login'i tamamen limitsiz bırakamaz.

Eklendi

  • Audit log artık rol/yetki değişikliklerini, Ayarlar'ı, API Client/Token'ları, paylaşım linklerini ve İçerik Dillerini kapsıyor — bunlar önceden bir log dosyasının dışında görünmüyordu (ya da hiç loglanmıyordu); artık ActivityLog yönetim ekranında görünüyorlar. Ayar değerleri asla loglanmıyor, yalnızca hangi anahtarların değiştiği kaydediliyor.
  • sk:install bir hatadan sonra devam edebiliyor — php artisan sk:install --resume, yarıda kalmış bir kurulumu tam olarak kaldığı yerden sürdürüyor; başarısız bir adım artık ham stack trace yerine net bir mesaj basıyor. Baştan bir Node.js sürüm kontrolü çalışıyor; eski/eksik Node artık kurulum ortasında kriptik bir çökme yerine uyarı üretiyor.
  • sk:doctor Node.js sürümünü ve bir queue worker'ın fiilen çalışıp çalışmadığını kontrol ediyor, cron heartbeat'i tespit edemediğinde artık sessizce "OK" demiyor. Tekil kontroller artık tüm komutu asılı bırakmak yerine zaman aşımına uğruyor.
  • sk:eject artık bir domain'i eject etmeden önce onay istiyor (--force/--dry-run/--no-interaction verilmediği sürece) — eject etmek domain'in kit güncellemelerini almayı bırakması demek; artık sessiz, tek yönlü bir kapı değil.
  • Datatable klavyeyle erişilebilir — sıralanabilir başlıklar, arama-temizle butonu ve filtre-kaldırma butonları artık Tab + Enter/Space ile çalışıyor; boş bir tablo, hiç veri olmadığı için mi yoksa filtrenizin hiçbir sonuç bulamadığı için mi boş olduğunu söylüyor (tek tıkla "filtreleri temizle" ile).
  • Formlar daha güvenli — çift gönderim artık imkânsız, kaydedilmemiş değişiklikli bir formdan çıkmak onay istiyor, başarısız bir veri/seçenek yüklemesi sessizce başarısız olmak yerine yeniden deneme seçeneği gösteriyor, zorunlu alanlar screen reader'lara bildiriliyor.
  • FileManager, birden çok eşzamanlı yükleme genelinde toplam ilerleme gösteriyor, görsel lightbox'ı da ok tuşlarıyla görseller arası gezinmeyi destekliyor.

Değiştirildi

  • CI artık lint hatalarında build'i sadece uyarmak yerine başarısız kılıyor.
  • --no-interaction kurulumları artık eski sabit password yerine taze, rastgele bir admin parolası alıyor (sonunda ekrana basılır).
  • Görünür davranış değişikliği olmayan birkaç backend tutarlılık temizliği (merkezi 422 hata maplemesi, paylaşılan definition-controller mantığı, birleştirilmiş definition cache invalidation).

2026-07-03 — v13.6.7

Rich-text editor'daki boş alan artık tıklanabiliyor

Tek hedefli bir CSS düzeltmesi — API veya kurulum değişikliği yok.

Düzeltildi

  • Rich-text editor'da son metin satırının altına tıklamak hiçbir şey yapmıyordu — EditorInput.vue minHeight'ı editor sarmalayıcısına inline min-height olarak veriyordu, ama içteki ProseMirror elemanı height: 100% kullanıyordu. Yüzde yükseklikler yalnız kesin yüksekliği olan bir ebeveyne karşı çözülür; bu yüzden ProseMirror yalnız kendi içeriği kadar büyüyordu — görsel olarak uzun kutunun geri kalanı gerçek contenteditable bölgesinin dışında kalıyor, oraya tıklama/yazma yok sayılıyordu. Sarmalayıcı artık flex column, ProseMirror da height: 100% yerine flex-1 kullanıyor; böylece düzenlenebilir alan tüm yapılandırılmış yüksekliği dolduruyor.

2026-06-20 — v13.6.6

Activity log artık UUID ve sayısal subject'leri birlikte kabul ediyor

Tek hedefli bir veritabanı düzeltmesi — API veya kurulum değişikliği yok.

Düzeltildi

  • sk:seed-permissions artık uuid cast hatasıyla çökmüyor — activity-log tablosu polimorfik subject_id / causer_id kolonlarını native uuid olarak oluşturuyordu. Ama kit aktiviteyi hem User (uuid anahtar) hem de Spatie Permission / Role modelleri (sayısal/bigint anahtar) üzerinde logluyor; bu yüzden permission seed'i SQLSTATE[HY000] 4078: Cannot cast 'bigint' as 'uuid' hatasıyla başarısız oluyordu. Yeni bir migration her iki id kolonunu char(36)'ya genişletiyor; bu hem 36 karakterlik uuid'yi hem de herhangi bir sayısal id'yi saklar — tek polimorfik kolon artık her denetlenen modele uyuyor. Migration önceki tüm durumları (native uuid, legacy bigint, legacy char(36)) char(36)'ya yakınsıyor; mevcut uygulamalar düzeltmeyi bir sonraki php artisan migrate'te alır.

2026-06-14 — v13.6.5

Çeviri bundle'ı artık paketle birlikte geliyor

Paketleme düzeltmesi — API veya kurulum değişikliği yok.

Düzeltildi

  • Taze kurulumlar artık ham çeviri anahtarı göstermiyor — önceden derlenmiş kit çeviri bundle'ları (resources/js/lang/php_{en,tr}.json) .gitignore'daydı; bu yüzden hiç Git'e girmiyor ve Composer dist'inde (yalnızca tracked dosyaların git archive'ı) bulunmuyordu. Taze kurulan bir uygulama bundle'ları değil yalnızca build script'ini alıyordu; sonuçta tüm kit i18n anahtarları (sk-menu.*, sk-setting.*, …) çevrilmiş etiket yerine ham anahtar olarak render ediliyordu. İki bundle artık tracked ve shipped. Consumer paketi build etmediği için — consumer-build theme bundle'ının aksine — bunların vendor/'a ulaşması için commit edilmesi gerekir; build script'in kendi dökümanı zaten "COMMITTED and shipped" diyordu.

2026-06-14 — v13.6.4

Datatable inline filtre dropdown düzeltmesi

Tek hedefli bir düzeltme — API veya kurulum değişikliği yok.

Düzeltildi

  • Inline filtre dropdown'u artık kesilmiyor — bir select filtresinin inline pill menüsü tablo kartının içinde absolute öğe olarak çiziliyordu; bu yüzden uzun seçenek listesi kart / scroll-container overflow kenarında kesiliyordu. Menü artık <body>'ye fixed overlay olarak teleport ediliyor (PrimeVue Select'in appendTo ile yaptığının aynısı): trigger'ından konumlanıyor, scroll/resize'da yeniden hizalanıyor, min(60vh, 420px) ile sınırlanıp kendi scroll'una sahip oluyor, dış-tık / Escape ile kapanıyor. panel yerleşimli popover variant'ı değişmedi (zaten PrimeVue'nun overflow-visible portalını kullanıyor).

2026-06-13 — v13.6.3

Admin arayüz rötuşları

Yönetim panelinde bir dizi arayüz iyileştirmesi — API veya kurulum değişikliği yok.

Değiştirildi

  • Aura sidebar footer artık bir sürüm pill'i — aura teması sidebar footer'ı tek satırlık pill kart oldu: solda yeşil durum noktası ve uygulama adı, sağ kenara yaslanmış sürüm (monospace). Sol/sağ boşluğu üstündeki nav item kartlarıyla aynı hizada. Yalnızca aura temasına özel; main teması footer'ı değişmedi.
  • Hesap menüsünde normal linklerden dış-bağlantı oku kalktı — üst bar kullanıcı/hesap menüsünde sıradan link öğelerinde (Profilim, Hesap Ayarları, Şifre Değiştir, Yardım, Çıkış) hover'daki ↗ oku artık gösterilmiyor. Açılır (alt menü) öğeleri chevron'unu, aktif dil de tik işaretini korur.
  • Datatable filtre popover'ı yalnızca panel filtreleri için — funnel butonu ve popover'ı yalnızca panel yerleşimli bir filtre varsa görünüyor; inline() filtreler artık popover içinde tekrarlanmıyor. Aktivite Kayıtları sayfasında üç filtre de (Olay, Model, Tarih) artık toolbar'da inline; bu sayfada funnel/popover tamamen kalktı.

Düzeltildi

  • sk:install / sk:update banner sürüm etiketi — kurulum/güncelleme başlığı artık v13.6.x yazıyor (bayat v13.5.x idi). Yalnızca kozmetik; geçmiş v13.5.0+ davranış notları değişmedi.
  • Datatable value modu tag'leri i18n anahtarını render anında çözüyor — tagLabels() değerleri artık builder kurulurken değil, hücre render edilirken çevriliyor. Builder bir sayfanın <script setup> gövdesinde, i18n bundle yüklenmeden önce kuruluyor; oradaki eager trans() ham anahtarı donduruyordu (İçerik Dilleri tablosu "Soldan sağa (LTR)" yerine sk-content-languages.directions.ltr gösteriyordu). Düz (anahtar olmayan) etiketler etkilenmez — trans() onları değiştirmeden döndürür.
  • İçerik Dilleri formu — yersiz zorunlu yıldızları kaldırıldı — FormBuilder alanları varsayılan olarak zorunlu, bu yüzden flag, fallback_code ve sort_order (sunucuda hepsi nullable) kırmızı * çiziyordu. Artık .optional() işaretli, validation kurallarıyla uyumlu; code, name, native_name, direction yıldızı korur.

2026-06-13 — v13.6.2

Admin panel layout ve form hizalama düzeltmeleri

main teması için bir dizi görsel düzeltme — API veya kurulum değişikliği yok, yalnızca görsel doğruluk.

Düzeltildi

  • Roller formu temel bilgiler 3 kolonlu responsive grid — ad / görünen ad / etiket rengi alanları yan yana dizilir (FB.form().cols(3)); küçük ekranlarda hep tam genişlik yerine alt alta yığılır.
  • İzin tablosu kart kenarına yaslı — roller izin matrisi SkCard flush prop'unu kullanır; satır border'ları body padding'i içinde yüzmek yerine kart kenarına ulaşır (hücreler kendi iç boşluğunu korur).
  • Translatable alan input'ları komşularıyla hizalı — dil sekmesi pill'leri (TranslatableInput) düz label'dan yüksekti ve input'u aşağı itiyordu; pill'ler artık düz-label yüksekliğine eşit, böylece grid satırındaki her input aynı hizada başlar.
  • Sidebar artık satırları ezmiyor — birden çok menü grubu açıkken nav, scroll'a düşmeden çocukları sıkıştırıyordu; doğrudan çocuklar artık shrink-0, taşma üst üste binmek yerine scroll olur.
  • Sidebar footer'ı sayfa footer'ıyla hizalı — sidebar footer yüksekliği h-footer (56px) ile sabitlendi; üst border'ı ekran altındaki sayfa footer border'ıyla aynı hizaya gelir.

Değişti

  • Güvenlik ayarları alt sekmesi "Cloudflare Turnstile" → "Bot Protection" — güvenlik alt sekmesi etiketi (EN/TR) ve ilgili SecurityTab bölümü artık sağlayıcıdan bağımsız adı kullanır.

2026-06-13 — v13.6.1

sk:update bayat bileşen import'larını kendiliğinden onarıyor

Tek hedefli bir düzeltme — API veya kurulum değişikliği yok.

Düzeltildi

  • sk:update artık bir bileşen vendor'a taşındıktan sonra bayat import bırakmıyor — bir bileşen stub'lardan @lvntr/components'e taşındığında eski yerel kopyası force-delete ediliyor, ama silinen yerel yolu hâlâ import eden kullanıcı-özelleştirmeli sayfalar dokunulmadan kalıyor ve Vite build'ini ENOENT load-fallback hatasıyla kırıyordu (örn. @/components/Auth/TurnstileWidget.vue). sk:update artık bu tür bayat import specifier'larını resources/js genelinde vendor yoluna (@lvntr/components/ui/TurnstileWidget.vue) yeniden yazıyor; böylece v13.6.0'da başlayan migrasyon, mevcut kullanıcıların özelleştirdiği Auth sayfalarında (Login, Register, ForgotPassword) tamamlanıyor.

2026-06-13 — v13.6.0 (devam)

Vendor-first Faz 2 — Settings-sekmesi controller'ları, Definitions/Media, ContentLanguage

Faz 2, Faz 1'de başlayan vendor-first taşımayı; vendor Settings sekmelerini destekleyen kalan controller'ları artı iki API/Service controller'ını taşıyarak ve ContentLanguage domain'ini tamamen vendorize ederek tamamlıyor. Vue ve migration'lar zaten vendor'daydı — bu yalnızca PHP katmanı taşıması. Sıfır kurulumlar bu dosyaların app kopyasını almaz; mevcut kurulumlar aynı hash guard altında sk:update ile taşınır.

Değişti

  • ApiClient, ApiToken, SystemHealth, ContentLanguage, Definitions (Api + Service) ve MediaUpload için vendor-first HTTP katmanı — bu controller'lar (varsa FormRequest / API Resource'larıyla birlikte) artık Lvntr\StarterKit\Http\... altında yaşıyor ve geriye dönük uyumluluk için App\Http\... FQCN'lerine alias'lanıyor. Bir app kopyası alias'ı otomatik olarak devre dışı bırakır; böylece özelleştirmeniz kazanmaya devam eder. Route adları, permission anahtarları ve Passport secret'ının tek-seferlik gösterimi değişmedi.
  • ContentLanguage domain'i vendorize edildi — Actions / DTOs / Queries Lvntr\StarterKit\Domain\ContentLanguage\ altına taşındı. App\Models\ContentLanguage modeli app-sahipli kalır (asla alias'lanmaz — policy discovery + route-model binding'i korur); vendor kodu ona App\ FQCN'iyle referans verir.

Eklendi

  • sk:eject beş yeni giriş kazandı — SystemHealth, ContentLanguage, Definitions, MediaUpload ve (ApiToken controller/request/resource'unu da eject eden) tam-HTTP-katmanlı bir ApiClient. Eject edilebilir domain sayısı 10'dan 14'e çıkıyor.

Migrasyon

composer update lvntr/laravel-starter-kit && php artisan sk:update çalıştırın. Bkz. docs/UPGRADE.tr.md (v13.5.11 → v13.6.0, "Behavior-module HTTP katmanı vendor'a taşındı — Faz 2").


2026-06-13 — v13.6.0 (devam)

Behavior-module HTTP + Vue katmanları vendor'a taşındı; sk:eject artık Files'ı destekliyor

Beş yerleşik yönetim modülü — Files, Logs, ActivityLogs, ApiRoutes, Settings — artık controller'larını, FormRequest'lerini ve Vue yönetim sayfalarını tamamen vendor paketinden çalıştırıyor. Sıfır kurulumlar bu modüllerin app kopyasını almaz. Mevcut kurulumlar hash guard altında sk:update ile taşınır (değişmemiş kopyalar kaldırılır; değiştirilmiş kopyalar korunur ve raporlanır). Vue migrasyonu ayrıca app.ts'te @lvntr/pages vendor-fallback glob'unu gerektirir.

Eklendi

  • sk:eject Files — FileManager yönetim Vue sayfalarını (resources/js/pages/Admin/Files/) UI özelleştirmesi için app'inize eject eder. FileManager backend'i (controller, FormRequest'ler, route-registry altyapısı) her zaman vendor-yönetimli kalır; yalnızca Vue katmanı kopyalanır. Geri almak kopyalanan sayfaları siler; vendor kopyası app.ts fallback'i üzerinden devam eder.
  • Logs, ActivityLogs, ApiRoutes, Settings için vendor-first HTTP katmanı — controller'lar ve FormRequest'ler artık Lvntr\StarterKit\Http\... altında yaşıyor ve geriye dönük uyumluluk için App\Http\Controllers\Admin\*'e alias'lanıyor. App'inizdeki bir app/Http/Controllers/Admin/SomeController.php dosyası alias'ı otomatik olarak devre dışı bırakır; böylece kopyanız kazanmaya devam eder.
  • sk:update'te grup-atomik migrasyon — vendor-first modüller katman bazında (PHP ve Vue bağımsız olarak) taşınır. Bir katmandaki herhangi bir dosya değiştirilmişse, o katmanın tamamı korunur. Hiçbir zaman yarı silinmiş bir modül üretilmez.

Değişti

  • sk:eject manifest'i genişledi — Files domain'i eklendi (yalnızca Vue: backend: ''). Komut imzasındaki eject edilebilir domain listesi artık Files'ı içeriyor.

Migrasyon

composer update lvntr/laravel-starter-kit && php artisan sk:update && npm run build çalıştırın. Özelleştirilmiş bir modül için sk:update onu korur ve raporlar; tam açık sahiplik almak için sk:eject <Module> çalıştırın. Üç senaryolu rehber için bkz. docs/UPGRADE.tr.md (v13.5.11 → v13.6.0, "Behavior-module HTTP + Vue katmanları vendor'a taşındı").


2026-06-11 — v13.6.0 (devam)

Kurulum anında User + Role domain eject'i

Sıfır kurulumlar artık User ve Role domain runtime'ını otomatik olarak app/Domain/ altına eject eder. Bu iki domain gerçek projelerde en çok özelleştirilen alanlardır; dolayısıyla ek bir adım gerekmeksizin proje sahipli dosyalar olarak gelirler.

Sıfır kurulumda ne değişir

  • app/Domain/User/ ve app/Domain/Role/, backend sınıfları (App\Domain\ namespace'iyle yeniden yazılmış Actions, DTOs, Queries, Events, Listeners) ile oluşturulur.
  • DomainServiceProvider, altı audit event için Event::listen binding'lerini alır; aktivite kaydı kesintisiz devam eder.
  • Sonraki composer update çalıştırmalarında bu dizinlere dokunulmaz — dosyalar size aittir.

Devre dışı bırakma

php artisan sk:install --without-eject

Her iki domain vendor'da kalır ve class_alias ile çözülür. sk:eject User / sk:eject Role komutlarını istediğiniz zaman manuel olarak çalıştırabilirsiniz.

Kurulumdan sonra geri alma

app/Domain/User/ ve app/Domain/Role/ dizinlerini silin, app/Providers/DomainServiceProvider.php içinden enjekte edilen Event::listen satırlarını kaldırın ve composer dump-autoload çalıştırın.

Mevcut kurulumlar

Değişiklik yok. Eject adımı yalnızca storage/starter-kit/hashes.json henüz yoksa (ilk kurulumda) çalışır. Mevcut kurulumda registry zaten mevcut olduğundan adım atlanır. Mevcut projeler etkilenmez.

sk:eject'e yeni flag

sk:eject, installer tarafından dahili olarak kullanılan --skip-autoload flag'ini kazandı; bu sayede installer her domain başına composer dump-autoload koşturmaz (tüm eject'ler tamamlandıktan sonra tek bir toplu dump gerçekleştirir). Bu flag normal manuel sk:eject kullanımında gerekmez.


2026-06-06 — v13.6.0

Minor sürüm — Vendor-runtime migrasyonu tamamlandı + yapılandırılmış tema/layout/CSS sistemi

13.6.0, son yayınlanan sürümden (v13.5.11) bu yana yapılan tüm değişiklikleri tek sürümde toplar. "Paket runtime'ı vendor'dan çalışır" migrasyonunu hem backend hem frontend'de tamamlar ve admin-panel layout'unu ve CSS'i yapılandırılmış, override edilebilir bir tema sistemine yeniden düzenler. Görsel değişiklik yok — varsayılan build (VITE_SK_THEME=main) v13.5.11 ile byte-identical'dır. Aşağıdaki bölümler toplanan değişiklikleri alana göre gruplar.

İzin direktif plugin'i vendor'dan çözülüyor

v-can / v-role Vue plugin'i (resources/js/plugins/permission.ts) artık varsayılan olarak vendor paketinden sunuluyor — kit composable'larının zaten kullandığı çözümün aynısı. @/plugins/<name> import'u yerel bir kopya varsa ona, yoksa vendor kopyasına düşer; böylece kit, stub yeniden kopyalamadan direktif düzeltmeleri gönderebilir. Davranış değişikliği yok: direktifler aynı ve app.ts hâlâ @/plugins/permission'ı değişmeden import ediyor.

Değişti

  • resources/js/plugins/permission.ts — vendor paketine taşındı. Ölü, kullanılmayan useCan() export'u kaldırıldı (canlı composable @/composables/useCan'dir); dosya artık yalnızca PermissionPlugin'i (v-can / v-role direktifleri) içeriyor, auto-import bağımlılığı yok.
  • vite.config.ts — @/composables/*'ı aynalayan yeni @/plugins/* alias customResolver'ı (resolvePlugin): önce yerel-override, sonra vendor fallback; düz @ alias'ından önce sıralı.
  • tsconfig.json — yeni @/plugins/* path eşlemesi (yerel + vendor).

Eklendi

  • sk:publish --tag=plugins — izin direktiflerini özelleştirmek için Vue plugin'lerinin yerel, düzenlenebilir bir kopyasını publish eder.

Geçiş

İşlem gerekmez — çözüm otomatik. Mevcut resources/js/plugins/permission.ts'iniz vendor kopyasını gölgelemeye devam eder; vendor sürümünü almak için silin. UPGRADE.md'ye bakın.


Tüm CSS cascade katmanları artık override edilebilir slot

Tema sistemindeki her CSS katmanı artık override edilebilir bir slot'tur. Daha önce fonts.css, _base.scss, _auth.scss ve utilities.css, resolver'ın dışında sabit import'lardı; artık themes/main/ altında yaşıyor ve scripts/sk-theme-build.mjs tarafından tokens, layout/* ve components/* ile birlikte doğru cascade sırasında emit ediliyor. Görsel değişiklik yok — VITE_SK_THEME=main ile varsayılan build v13.5.11 ile byte-identical'dır. Tek fark, custom temanın artık themes/custom/ altına eşleşen bir dosya bırakarak fonts, base reset, auth stilleri ve utility override'ları dahil her katmanı override edebilmesidir.

Değişti

  • themes/main/fonts.css, themes/main/_base.scss, themes/main/_auth.scss, themes/main/utilities.css — resources/css/theme/ kökünden themes/main/ dizinine taşındı. İçerik değişmedi.
  • scripts/sk-theme-build.mjs — HEAD slot'ları (tokens.css, fonts.css, _base.scss) ve TAIL slot'ları (_auth.scss, utilities.css) artık resolveSlot() üzerinden çözümleniyor (layout/* ve components/* için kullanılan override-veya-main fallback ile aynı). Cascade sırası korunur: tokens → fonts → _base → layout/* → components/* → _auth → utilities.
  • theme/theme.css — artık yalnızca @import './_active.css' içeriyor; eski sabit _auth.scss import'u kaldırıldı.
  • app.css — eski sabit utilities.css tail import'u kaldırıldı; utilities.css artık resolver'ın emit ettiği son slot'tur.
  • themes/custom/README.md — fonts.css, _base.scss, _auth.scss ve utilities.css dahil tüm override edilebilir slot'ları listeleyecek şekilde güncellendi.

Geçiş

Herhangi bir işlem gerekmez. sk:update güncellenmiş dosyaları iletir. Varsayılan build byte-identical'dır. Daha önce sabit olan bir katmanı override etmek için themes/custom/ altına eşleşen dosyayı bırakın (örn. themes/custom/fonts.css). Bkz. docs/theme.tr.md — Tam slot referansı.


AppShell layout kompozisyonu + build-zamanı tema-override sistemi (themes/main / themes/custom)

Admin panel layout'u ve CSS'i yapılandırılmış, override'a hazır bir sisteme yeniden düzenlendi. Görsel değişiklik yok — varsayılan build önceki yayınlanan sürümle byte-identical'dır. Layout kabuğu, yeniden kullanılabilir bir AppShell.vue (yapısal omurga, sidebar durumu, adlandırılmış bölgeler) ve standart admin bileşenlerini bağlayan ince bir AdminLayout.vue kompozisyonuna bölünür. CSS monoliti (_admin.scss ve dağınık _*.scss partial'ları) ayrı slot dosyalarından oluşan bir themes/main/ dizin ağacına dönüştürülür. Yeni opt-in themes/custom/ dizini ve scripts/sk-theme-build.mjs tema resolver'ı, build zamanında slot bazında override'a olanak tanır: VITE_SK_THEME=custom ayarlayın, themes/custom/components/datatable.css dosyası ekleyin — yalnızca o slot değiştirilir, geri kalanı main'e döner. Tam referans ve özel override reçetesi için bkz. docs/theme.tr.md.

Eklendi

  • AppShell.vue (resources/js/layouts/AppShell.vue) — yeniden kullanılabilir yapısal layout kabuğu. .admin-layout / .admin-main / .admin-content iskeletini ve useSidebar durumunu sahiplenir (tek sahip). Beş adlandırılmış slot sunar: #sidebar (scoped: collapsed, mobileOpen, isMobile, closeMobile), #header (scoped: collapsed, isMobile, toggle), default, #footer, #overlays.
  • themes/main/ CSS ağacı — tokens.css (CSS custom property'leri, aydınlık + karanlık), layout/{shell,sidebar,header,page-header,footer}.css, components/{card,confirm,datatable,dialog,editor,formbuilder,menus,navigation,primevue,tabs,tag,toast}.css. Değerler kaldırılan partial'larla byte-identical'dır.
  • themes/custom/ iskeleti — tam-değiştirme + fallback modelini açıklayan README.md ile birlikte boş override-tema dizini.
  • scripts/sk-theme-build.mjs — tema resolver'ı. VITE_SK_THEME'yi okur (varsayılan main); kanonikleri slot listesi için themes/main/'i dolaşır; her slot için varsa themes/<aktif>/<slot>, yoksa themes/main/<slot> emit eder; theme/_active.css'i yazar. Override slot'ları çıktıda /* override */ ile işaretlenir. dev ve build'e açık && adımı olarak zincirlenir — npm lifecycle hook kullanılmaz — bu nedenle ignore-scripts=true altında da doğru çalışır.
  • npm run theme:build — resolver için bağımsız script alias'ı (dev ve build'in açık bir adımı olarak da çalışır).
  • VITE_SK_THEME=main satır içi dokümantasyonuyla .env.example'a eklendi.
  • PrimeVue preset resolver — scripts/vite-plugin-sk-theme.mjs artık @/theme/preset import'unu build zamanında yakalar; resources/js/theme/themes/<aktif>/preset.ts mevcutsa ona, yoksa taban resources/js/theme/preset.ts'e çözümler. Taban dosya yerinde kalır — consumer geçiş adımı gerekmez. resources/js/theme/themes/custom/ iskeleti boş gelir; varsayılan build önceki sürümle byte-identical'dır.
  • docs/theme.md + docs/theme.tr.md — güncellendi: iki katmanlı genel bakış tablosu (CSS override ve PrimeVue preset), PrimeVue preset katmanı bölümü (dizin düzeni, custom palet reçetesi ve bağımlılık zinciri notu — tokens.css --p-* değişkenlerini okur).

Değişti

  • AdminLayout.vue ince bir AppShell kompozisyonuna dönüştürüldü. Dış prop/slot kontratı (title, subtitle, backUrl, default, page-actions) değişmedi — mevcut tüm sayfalar değişiklik gerektirmeden çalışmaya devam eder.
  • theme.css artık açık bir partial listesi yerine tek bir _active.css import eder. Import sırası korunur.
  • _base.scss yalnızca base/reset kurallarını barındırır; :root / .dark CSS custom-property blokları themes/main/tokens.css'e taşındı.

Kaldırıldı

  • _admin.scss — themes/main/layout/* ile değiştirildi.
  • _datatable.scss, _formbuilder.scss, _dialog.scss, _toast.scss, _tag.scss, _card.scss, _editor.scss, _tabs.scss, _menus.scss, _navigation.scss, _confirm.scss, _primevue.scss — themes/main/components/* ile değiştirildi.

Düzeltildi

  • Tema resolver'ı artık ignore-scripts=true altında da çalışıyor — resolver doğrudan dev ve build script'lerine zincirlendi (node scripts/sk-theme-build.mjs && vite …). Daha önce predev / prebuild lifecycle hook'ları olarak çalışıyordu; npm bu hook'ları ignore-scripts=true ayarlandığında (consumer projelerde ve CI'da yaygın) sessizce atlıyor, bu da _active.css'in oluşturulmamasına ve build'in hard-fail vermesine neden oluyordu. predev ve prebuild girdileri kaldırıldı.

Geçiş

sk:update tüm yeni stub'ları iletir. Taşınan dosyalardan hiçbiri özelleştirilmediyse geçiş adımı gerekmez — npm run build byte-identical panel üretir. Taşınan bir dosyayı özelleştirdiyseniz, değişikliklerinizi ilgili themes/main/ slot'una kopyalayın veya izole bir override için themes/custom/ kullanın. Ayrıntılar için bkz. docs/UPGRADE.tr.md (v13.5.11 → v13.6.0).


Kit composable'ları vendor'dan çalışıyor; local-first resolver; sk:publish --tag=composables

v13.5.12 ile 15 kit composable'ı stub scaffold'undan çıkarılarak vendor kütüphanesine taşındı. Artık doğrudan vendor/lvntr/laravel-starter-kit/resources/js/composables/ üzerinden çalışıyor ve her composer update ile güncelleniyor. Import yolları tamamen değişmedi — @/composables/<name> önce local dosyayı kontrol eder (varsa tüketici dosyası kazanır), yoksa vendor kopyasına döner; bu nedenle hiçbir import ifadesinin değişmesi gerekmez. useAdminMenu ve index.ts, tüketicinin ürettiği route dosyalarına ve projeye özgü menü tanımına bağımlı olduğundan düzenlenebilir stub olarak kalmaya devam eder. TurnstileWidget.vue da aynı sürümde vendor kütüphanesine taşındı (@lvntr/components/ui/TurnstileWidget.vue).

Eklendi

  • Vendor'da 15 composable — useApi, useCan, useConfirm, useDarkMode, useDatatableSelection, useDefinition, useDialog, useFileShare, useFlash, useImageLightbox, useMenuBuilder, usePageLoading, useRefreshBus, useSidebar, useUrlTab pakete dahil edildi. composer update ile güncellenir — elle dosya yönetimi gerekmez.
  • sk:publish --tag=composables — vendor composable'larını özelleştirme için resources/js/composables/ dizinine kopyalar. Local-first resolver local kopyayı otomatik olarak seçer; alias veya build config değişikliği gerekmez.
  • TurnstileWidget.vue vendor'a taşındı — artık @lvntr/components/ui/TurnstileWidget.vue üzerinden kullanılabilir.

Kaldırıldı

  • 15 composable stub — scaffold'dan kaldırıldı. Mevcut projeler etkilenmez (local-first resolver local kopyaları kullanmaya devam eder). Vendor tarafından yönetilen güncellemelere geçmek için: özelleştirmediğiniz composable dosyalarını resources/js/composables/ dizininden silin; useAdminMenu.ts, index.ts ve düzenlediğiniz dosyaları koruyun.

Backend runtime sınıfları ve üçüncü-parti config'ler vendor'dan çalışıyor

Aynı sürüm, v13.5.0'daki "runtime vendor'dan çalışır" geçişini backend tarafında sürdürür. Bir grup yardımcı sınıf, validation kuralı ve middleware publish edilen scaffold'dan çıkıp vendor paketine taşındı; üç üçüncü-parti config dosyası artık app'inize kopyalanmıyor. Mevcut app'ler etkilenmez — App\… import'ları çözülmeye devam eder (tam taşınanlar için class_alias, geri kalanlar için ince bir App\ shim), ve önceden publish ettiğiniz bir config kazanmayı sürdürür. Tek zorunlu adım composer update'tir. Tam geçiş rehberi için bkz. docs/UPGRADE.tr.md (v13.5.11 → v13.6.0).

Eklendi

  • Vendor-resident backend sınıfları — HtmlSanitizer, TranslatableQueryHelpers, MediaPathGenerator, Scramble\ApiResponseExtension ve AssignTraceId / SetLocale / ValidateTurnstile middleware'leri artık Lvntr\StarterKit\*'ten çalışır. App'e stub kopyalanmaz; eski App\… import'ları class_alias ile çözülür. ApiResponseExtension artık Scramble'a düzgün kaydedilir.
  • İnce App\ shim'li vendor sınıfları — DatatableQueryBuilder, HttpsOrLocalhostUrl ve TurnstileRule vendor'dan çalışırken App\… import yolunu korur.
  • HasTranslatableRules trait'i → vendor (doğrudan import) — artık Lvntr\StarterKit\Support\HasTranslatableRules. Trait'ler alias'lanamadığı için vendor namespace'inden import edin (HasActivityLogging / HasMediaCollections ile aynı konvansiyon).

Değişti

  • Üçüncü-parti config override'ları runtime'da — config/activitylog.php, config/inertia.php ve config/media-library.php artık publish edilmiyor. StarterKitServiceProvider::applyVendorConfigDefaults() yalnızca kit'in gerekli anahtarlarını (media-library path_generator + media_model, activitylog include_soft_deleted_subjects, inertia ssr.enabled) runtime'da uygular ve publish ettiğiniz config'i atlar. Installer artık media-library path generator'ı AST ile enjekte etmez.

Kaldırıldı

  • Backend scaffold stub'ları — app/Support/{HtmlSanitizer,TranslatableQueryHelpers,MediaPathGenerator,HasTranslatableRules}.php, app/Support/Scramble/ApiResponseExtension.php, app/Http/Middleware/{AssignTraceId,SetLocale,ValidateTurnstile}.php ve config/{activitylog,inertia,media-library}.php scaffold'dan kaldırıldı. Yükseltilen app'ler mevcut kopyaları korur (sk:update bilgilendirme bildirimi gösterir, asla otomatik silmez). HasTranslatableRules trait'i için yerel kopyayı silmeden önce use import'larını vendor namespace'ine çevirin.

2026-06-04 — v13.5.11

Yama sürüm — Monolit skill kaldırıldı, yerine bağımsız 3-skill seti eklendi

v13.5.11 ile 723 satırlık monolit skill dosyası (stubs/.claude/skills/lvntr-starter-kit/SKILL.md) kaldırıldı ve yerine stubs/.claude/skills/ altında dağıtılan üç odaklı, kendi kendine yeten skill geldi. Yeni skill'ler ek bir araç gerektirmeden çalışır ve starter-kit projesinin üç ana alanını kapsar: çekirdek kurallar, backend/DDD ve frontend builder kalıpları.

sk:install komutuna --without-ai-skill flag'i eklendi; bu flag ile skill dosyalarının host uygulamaya yayımlanması atlanabilir.

Eklendi

  • stubs/.claude/skills/lvntr-starter-kit/ — çekirdek skill: zorunlu kurallar, reçete pointer'ları, permissions/i18n yapılandırması, alan arası references/ bağlantıları.
  • stubs/.claude/skills/lvntr-kit-domain/ — backend / DDD skill: Action, Service, FormRequest, Resource, Repository kuralları ve domain sınır rehberi.
  • stubs/.claude/skills/lvntr-kit-frontend/ — frontend skill: FormBuilder / DatatableBuilder / TabBuilder kalıpları, composable'lar (useApi, useDialog, useForm) ve starter-kit bileşen kuralları.
  • sk:install --without-ai-skill — opt-out flag'i; skill dosyalarının host uygulamaya yayımlanmasını atlar.

Kaldırıldı

  • stubs/.claude/skills/lvntr-starter-kit/SKILL.md — 723 satırlık monolit skill kaldırıldı. Bu dosyayı daha önce host uygulamanıza yayımladıysanız vendor:publish komutunu tekrar çalıştırmadan önce .claude/skills/lvntr-starter-kit/SKILL.md dosyasını silin.

2026-05-30 — v13.5.10

Yama sürüm — SkCard primitive, card başlığı sağ slot ve caption alt çizgisi

v13.5.10 ile PrimeVue Card'ı sarmalayan paylaşımlı bir wrapper olan SkCard eklendi; kit'in tüm card yüzeyleri için tek doğru kaynak. Aynı sürümde tüketiciye iki yeni slot açılıyor: SkForm'un root card'ında #title-end ve her FB.section() card'ında per-section #section-${key}-title-end. İkisi de başlığın sağına, aynı satırda render olur — action button, durum badge'i veya kontekste duyarlı gösterge için. Section slot'u scoped'tur ve { values } (mevcut form değerlerinin reaktif snapshot'ı) verir; tüketici koşullu render yapabilir. SkCard'ın kendi API'si: title, subtitle, transparent, divider ve pt prop'ları + header/title/subtitle/content/footer/title-end slot'ları; inheritAttrs: false + useAttrs kombinasyonu sayesinde dış class fallthrough'u Card root'una düşer (PrimeVue Card kendi root'unda inheritAttrs: false yaptığı için aksi halde class kaybolur). SkForm.vue ve SkFormFieldRenderer.vue <Card> yerine <SkCard> kullanacak şekilde refactor edildi — cardPt/transparentCard/sectionCardPt helper'ları kaldırıldı, title flex wrapper ve caption alt çizgi stilleri _formbuilder.scss'ten _card.scss'e taşındı (.sk-card--divider .p-card-caption). Artık SkCard ile sarmalanan herhangi bir tüketici aynı caption davranışını alır: başlık metni solda, #title-end sağda, alt başlık altta, caption bloğunun altına ayırıcı çizgi.

Eklendi

  • SkCard UI primitive — resources/js/components/Lvntr-Starter-Kit/ui/SkCard.vue. SkForm (ve gelecekte SkDatatable / sayfa düzeyi card'lar) tarafından kullanılması amaçlanan, PrimeVue Card etrafındaki paylaşımlı wrapper. Caption davranışı, #title-end slot'u ve alt çizgi tek implementasyondan geliyor.
    • Props: title?: string, subtitle?: string, transparent?: boolean (varsayılan false — true arka plan/shadow/padding'i kaldırır; dialog veya nested card için), divider?: boolean (varsayılan true — caption bloğunun altına alt çizgi), pt?: Record<string, any> (PrimeVue Card pt'sine merge edilir; consumer key'leri çakışmada kazanır).
    • Slot'lar: header, title, subtitle, content (default slot da content'e map'lenir), footer, title-end (sağa hizalı action/badge/durum slot'u).
    • inheritAttrs: false + useAttrs ile dış class fallthrough'u Card root'una geçer (PrimeVue Card kendi root'unda inheritAttrs: false yaptığı için aksi halde class düşmüyordu).
    • index.ts'ten SkCard olarak export edildi.
  • SkForm.vue — #title-end slot — form-level card başlığının sağına render edilen yeni slot. Başlık metniyle aynı satırda action button, badge veya durum göstergesi yerleştirmek için kullanılır. Slot yalnızca içerik verildiğinde render edilir.
  • SkFormFieldRenderer.vue — per-section #section-${key}-title-end slot — her section card başlığının sağına render edilen scoped slot. SkForm.vue zaten generic v-for $slots forwarding yaptığı için tüketici doğrudan <SkForm> üzerinden <template #section-address-title-end="{ values }"> şeklinde kullanır. Slot scope: { values } — mevcut form değerlerinin reaktif snapshot'ı, koşullu render için kullanışlı.
  • Docs — docs/ui-components.md ve docs/ui-components.tr.md'ye yeni "SkCard" bölümü; docs/formbuilder.md ve docs/formbuilder.tr.md'ye "Card Title Actions Slot" / "Card Başlık Sağ Slot" bölümü.
  • BaseFieldConfig.colSpan?: number — field'ın (ya da section içindeki field'ın) form grid'inde kaç sütun kaplayacağını belirtir (1..cols). Belirtilmezse mevcut davranış geçerli (1 hücre). cols değerini aşan değerler otomatik clamp'lenir; section içinde clamp, sectionCols'u referans alır.
  • BaseFieldBuilder.colSpan(n: number) — her field builder'ına zincirlenebilir .colSpan(n) eklendi. Örnek: FB.inputText().key('baslik').label('Başlık').colSpan(12).
  • SkColorSelector — 5 nötr Tailwind ailesi — tüm 50–950 shade'leriyle slate, gray, zinc, neutral, stone eklendi (Tailwind v4 resmi hex değerleri). Toplam palette: 22 aile.
  • stubs/components.d.ts — SkCard export tipi eklendi.

Değiştirildi

  • SkForm.vue — root <Card> → <SkCard> refactor — kendi içindeki cardPt computed'i ve transparentCard style sabiti kaldırıldı; :transparent="isTransparentCard" prop'u ile SkCard'a devredildi. Form card'ının başlık ve alt başlığı :title / :subtitle prop'larıyla geçirilir; flex title wrapper ve caption alt çizgisi SkCard içinde tek noktadan üretilir.
  • SkFormFieldRenderer.vue — section render'ı <SkCard>'a geçirildi — sectionCardPt yerine sectionIsTransparent helper'ı + :transparent prop'u kullanılır. Section title flex wrapper ve title-end slot'u SkCard'a delege edildi; icon'lu title (SkIcon + metin) doğrudan SkCard'ın #title slot'unda render edilir.
  • RenderCtx (SkFormFieldRenderer.vue) — transparentCard alanı kaldırıldı — SkCard'ın transparent prop'u tek doğru kaynak; ctx üzerinde dolaşan style sabiti gerek bırakmıyor.
  • stubs/resources/css/theme/_card.scss — SkCard stilleri eklendi:
    • .sk-card__title-row (flex w-full justify-between, başlık satırı)
    • .sk-card__title-text (başlık metni, ikon hizalama için inline-flex)
    • .sk-card__title-end (sağ slot kapsayıcısı, shrink-0)
    • .sk-card--divider .p-card-caption — caption bloğunun altına pb-3 mb-1 border-b + --p-surface-200 / --p-surface-700 dark varyant. Yalnız SkCard içinde tetiklenir, diğer PrimeVue Card kullanımlarını etkilemez.
  • stubs/resources/css/theme/_formbuilder.scss — bu çalışmanın ilk turunda eklenen geçici selektörler (.sk-fb__card*, .sk-fb__section-title-wrapper, .sk-fb__section-title-end, .sk-fb__card .p-card-caption, .sk-fb__section .p-card-caption) kaldırıldı. Yerine _card.scss'e işaret eden kısa bir not eklendi.
  • SkForm.vue — colsClassMap 1–12'ye genişletildi — 7–12 aralığı artık default grid'e düşmüyor; cols(7)–cols(12) doğrudan md:grid-cols-N uygular.
  • SkForm.vue + SkFormFieldRenderer.vue — colSpanClassMap — purge-safe statik map eklendi; üst seviye ve section içi field wrapper'ları colSpan değerine göre md:col-span-N alır. colSpan belirtilmemiş field'lar öncekiyle birebir render edilir (regression yok).

2026-05-21 — v13.5.9

Yama sürüm — SkIcon primitive, section/card gruplama ve icon API'leri

v13.5.9 ile SkIcon eklendi: tek icon: string prop'tan üç formatı otomatik algılayan paket-bağımsız bir icon renderer. Ham SVG → v-html, URL → <img>, diğer → <i :class> (PrimeIcons, FontAwesome, MDI, Lucide, Iconify ve diğer class tabanlı icon set'leri). BaseFieldConfig'e eklenen birleşik icon API tüm field tiplerine yayıldı: labelIcon / labelIconPosition her layout'ta label yanına icon koyuyor, icon / iconPosition input içine koyuyor (input-text, input-number, input-mask, password destekli). Başlık field'larına da kendi icon / iconPosition çifti eklendi. Öne çıkan özellik ise SectionFieldConfig (type: 'section') ve FB.section() fluent builder'ı — field'lar artık başlık, alt başlık, ikon ve ayarlanabilir kolon sayısıyla bir PrimeVue Card içinde görsel olarak gruplandırılabiliyor; form payload'ı flat kalmaya devam ediyor (section key'leri hiçbir zaman emit edilmiyor). SkForm.vue'ya iterateAllFields generator destekli flatFields computed eklendi; bu sayede section'lar tüm mevcut field işleme mantığı için (dosya upload, tarih dönüşümleri, definition preload, dynamic select) şeffaf. SkFormFieldRenderer.vue ayrıştırılarak recursive render ve slot forwarding bu bileşene taşındı. InputTextFieldConfig.icon / iconPosition artık deprecated; BaseFieldConfig seviyesindeki API kullanılmalı.

Eklendi

  • SkIcon UI primitive — paket-bağımsız icon renderer. Tek icon: string prop'tan otomatik algılama: <svg… → ham SVG (v-html), ^(https?:|data:) → <img>, diğer → <i :class> (PrimeIcons, FontAwesome, MDI, Lucide, Iconify ve diğer class tabanlı icon set'ler). Güvenlik: icon yalnızca builder config'ten (geliştirici kontrollü) geçirilmeli — kullanıcı kaynaklı string XSS riskidir (<svg… path'i v-html ile render eder).
  • BaseFieldConfig icon alanları — tüm field tipleri için ortak icon API'si:
    • labelIcon?: string + labelIconPosition?: 'left' | 'right' (varsayılan: 'left') — tüm layout path'lerinde label yanına icon.
    • icon?: string + iconPosition?: 'left' | 'right' (varsayılan: 'left') — input içine icon. Desteklenen tipler: input-text, input-number, input-mask, password (custom path — feedback: true ise icon yok). groupPrefix/groupSuffix önceliklidir; varsa input icon devre dışı kalır.
  • TitleFieldConfig icon alanları — icon?: string + iconPosition?: 'left' | 'right'. Örnek: FB.title('Genel').icon('pi pi-info-circle').
  • SectionFieldConfig (yeni field tipi type: 'section') — form içinde Card ile görsel field gruplama:
    • title? (translation key, label fallback), subtitle?, icon?, iconPosition?
    • cols?: number (varsayılan: parent formun cols değeri)
    • fields: FieldConfig[] (tek seviye nested — iç içe section desteklenmez)
    • isCard?: boolean (varsayılan: card görünür; false → şeffaf Card)
    • Form veri yapısı flat kalır — section'ın key'i payload'a girmez; section yalnızca görsel gruplama primitive'idir.
  • SectionBuilder ve FB.section(title?) factory — fluent API: .title(t), .subtitle(s), .icon(str), .iconPosition(p), .cols(c), .isCard(enabled), .addFields(...).
  • BaseFieldBuilder fluent metotları — .labelIcon(str), .labelIconPosition(p), .icon(str), .iconPosition(p) artık tüm field builder'larında mevcut (InputTextBuilder'dan base'e taşındı — imza aynı, davranış değişmedi).
  • TitleBuilder.icon() ve .iconPosition() metotları eklendi.
  • SkFormFieldRenderer.vue — ayrıştırılmış recursive field renderer. Section render, slot forwarding ve label/title icon render bu bileşene taşındı; SkForm.vue template'i basitleşti.
  • Docs — docs/formbuilder.md ve docs/formbuilder.tr.md'ye 5 yeni bölüm: İkonlar (Paket-Bağımsız), Label İkonları, Input İkonları, Başlık İkonları, Section / Card Gruplama. XSS güvenlik notu her iki dilde mevcut.

Değiştirildi

  • AppDialog.vue — confirmSeverity artık varsayılan 'primary' kullanmıyor — state.footer?.severity ?? 'primary' → state.footer?.severity. Onay düğmesi artık severity belirtilmediğinde PrimeVue Button'ın kendi varsayılan görünümünü kullanır (tema preset'inden gelir). DialogFooter.severity açıkça set edilmemiş mevcut dialog'lar görsel değişiklik yaşayabilir.
  • useDialog.ts — DialogFooterSeverity tipi genişletildi — 'primary' kaldırıldı (PrimeVue Button'da geçerli değil); 'info', 'help', 'contrast' eklendi. Tam liste: 'secondary' | 'success' | 'info' | 'warn' | 'help' | 'danger' | 'contrast'.
  • SkForm.vue — flat field iterasyonu — derivedDefaults, currentValues, definitionKeys, dynamicSelectFields, hasFileFields, dateOnlyFields computed'leri artık yeni flatFields computed'i (iteratif iterateAllFields generator) üzerinden çalışıyor. Section içindeki field'lar otomatik olarak doğru kategorize ediliyor (dosya upload existingMediaKey çözümü, tarih dönüşümleri, definition preload, dynamic optionsUrl fetch). Section içermeyen mevcut formlar birebir aynı render ediliyor (regression yok).
  • SkFormInput.vue — generic input icon — daha önce yalnızca input-text'te aktif olan IconField wrapping pattern'i artık input-number, input-mask ve password için de (custom path) aktif. Icon descriptor'lar SkIcon üzerinden render ediliyor; PrimeIcons dışında MDI/FA/Lucide/Iconify/SVG/img URL de çalışıyor. BaseFieldConfig.icon önceliklidir, InputTextFieldConfig.icon legacy fallback olarak korunur.
  • stubs/resources/css/theme/_formbuilder.scss — .sk-fb__title ve .sk-fb__label selector'larına icon hizalaması için minimal inline-flex items-center gap eklendi (line-height ve padding değişmedi). Yeni bölümler: SKICON & LABEL/TITLE ICONS (.sk-icon, .sk-icon--svg svg, .sk-icon--img, .sk-fb__label-icon, .sk-fb__title-icon, .sk-fb__section-icon + --left/--right modifier hook'ları), SECTION CARD (.sk-fb__section, .sk-fb__section-title, .sk-fb__section-field).

Deprecated

  • InputTextFieldConfig.icon ve InputTextFieldConfig.iconPosition — yeni BaseFieldConfig.icon ve BaseFieldConfig.iconPosition kullanın. Legacy alanlar geriye uyumluluk için korundu (SkFormInput.vue base ?? legacy fallback ile aynı render üretiyor), gelecek major versiyonda kaldırılacak.

Yükseltme

composer update lvntr/laravel-starter-kit

# Etkilenen stub'ları yeniden yayınla (DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın)
# stubs/resources/css/theme/_formbuilder.scss
# stubs/resources/js/composables/useDialog.ts  ← DialogFooterSeverity tipi değişti
php artisan vendor:publish --tag=starter-kit-stubs --force

DialogFooterSeverity breaking change: 'primary' artık geçerli bir değer değil. useDialog().open(...) çağrılarında severity: 'primary' kullandıysanız kaldırın (Button kendi tema varsayılanını uygular) ya da 'secondary' / 'contrast' gibi geçerli bir değerle değiştirin. TypeScript bu satırları zaten hata olarak işaretleyecektir.

Migration: legacy InputTextFieldConfig.icon çağrılarınız çalışmaya devam eder (deprecated, kaldırılana kadar fallback). Yeni özellikleri kullanmak için:

// Label icon — her field tipinde
FB.inputText().key('email').label('E-posta').labelIcon('pi pi-envelope')

// Input icon — input-text/number/mask/password
FB.inputText().key('search').icon('pi pi-search')                    // PrimeIcons
FB.inputText().key('user').icon('mdi mdi-account')                   // Material Design Icons
FB.inputText().key('star').icon('fa fa-star').iconPosition('right')  // FontAwesome
FB.inputText().key('logo').icon('https://cdn.example.com/icon.svg')  // URL

// Başlık icon'u
FB.title('Genel Bilgiler').icon('pi pi-info-circle')

// Section / Card gruplama
FB.form()
    .isCard(false)
    .addFields(
        FB.section('Kişisel Bilgiler').icon('pi pi-user').cols(2).addFields(
            FB.inputText().key('first_name').label('Ad'),
            FB.inputText().key('last_name').label('Soyad'),
        ),
        FB.section('Adres').icon('pi pi-map-marker').addFields(/* ... */),
    )
    .build();

2026-05-20 — v13.5.8

AppDialog, PrimeVue Dialog'un #container template'i etrafında bağımsız bir "Material Flat" shell olarak yeniden tasarlandı: header'da gradient ikon lozenge + başlık + alt başlık, opsiyonel slate-100 sticky footer (solda hint ikon/metin, sağda İptal/Onay butonları), daha yumuşak iki katmanlı drop shadow ve özel "rise" enter/leave animasyonu. Shell tamamen sk-dlg PT class'ı ile scope'lanmış durumda; ConfirmDialog ve diğer Dialog kullanımları etkilenmiyor. useDialog composable'ı subtitle, icon ve footer open option'ları, yeni DialogFooter interface'i ve setFooter() / patchFooter() metotları ile genişletildi — dialog içinde render olan bileşenler artık footer'ı (örneğin onay butonunu loading state'e geçirme) dialog'u yeniden açmadan değiştirebiliyor. v13.5.7'den kalan son sticky-bar sorunu — form scroll ettiğinde gri footer'ın sağında kalan ~10 px beyaz boşluk — dialog body'sinin scrollbar'ını görsel olarak gizleyerek çözüldü; scroll hâlâ wheel / trackpad / klavye ile çalışıyor, slate-100 bar artık dialog'un sağ kenarına temiz biçimde dayanıyor.

Eklendi

  • AppDialog Material Flat shell — header artık ikon lozenge (state.icon), başlık (state.header) ve alt başlık (state.subtitle) basıyor; PrimeVue'nun default close butonu yerine slate temalı bir close butonu kondu. Opt-in footer slate-100 sticky action bar üretiyor (hint ikon/metin + İptal/Onay).
  • useDialog zengin header & footer API — OpenOptions.subtitle, OpenOptions.icon, OpenOptions.footer eklendi. Yeni DialogFooter tipi: icon, text, cancelLabel, confirmLabel, confirmIcon, severity, onConfirm, hideCancel, disabled, loading. Yeni setFooter() ve patchFooter() metotları eklendi.
  • _dialog.scss — theme.css'ten import edilen yeni stylesheet. Shell parçalarını (mask, root, head/lead/title-block, body, foot/info/actions) tanımlıyor ve sk-dlg PT class'ı ile scope'lanmış durumda.

Değiştirildi

  • preset.ts modal token — borderRadius.xl → borderRadius.md (6 px), padding: 1.25rem → padding: 0 (shell-level padding artık AppDialog içinde), drop shadow daha yumuşak iki katmanlı versiyona güncellendi (0 24px 60px -20px ..., 0 6px 20px -6px ...).

Düzeltildi

  • Form scrollbar boşluğu (footer'ın sağında) — AppDialog içindeki uzun formlarda slate-100 action bar'ın sağ kenarı ile dialog'un sağ kenarı arasında ~10 px beyaz boşluk kalıyordu (body'nin scrollbar'ı içerik genişliğini yiyordu, bar'ın -mx-8 uzatması yalnızca body'nin content kenarına dayanıyordu). .sk-dlg__body:has(.sk-fb--dialog) artık scrollbar'ı görsel olarak gizliyor (scrollbar-width: none + ::-webkit-scrollbar { width: 0 }); scroll wheel / trackpad / yön tuşları / Page Up–Down / Home–End ile çalışmaya devam ediyor.

Yükseltme

composer update lvntr/laravel-starter-kit

# Etkilenen stub'ları yeniden yayınla (DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın)
# stubs/resources/css/theme/{_dialog.scss,_formbuilder.scss,theme.css}
# stubs/resources/js/composables/useDialog.ts
# stubs/resources/js/theme/preset.ts
php artisan vendor:publish --tag=starter-kit-stubs --force

Davranış notu: Form dialog'ları içinde Dialog body'sinin scrollbar'ı bilinçli olarak görünmez. Görünür track gizlendi ki slate-100 action bar dialog kenarına boşluksuz dayansın; scroll wheel, trackpad, yön tuşları, Page Up/Down, Home/End ile çalışmaya devam ediyor.


2026-05-19 — v13.5.7

Yama sürüm — Dialog sticky bar sızıntısı düzeltildi, AvatarUpload yenilendi, 14px root tipografi

AppDialog içindeki form sticky action bar'ı (Cancel / Update), uzun formlarda alttan akan içeriği gizleyemiyordu — temel sorun, PrimeVue Dialog content'inin default padding: 1.25rem değerinin sticky barın altında transparan bir boşluk bırakmasıydı. Dialog content padding-bottom PT API ile sıfırlandı, SkForm artık dialog mode'da sk-fb--dialog marker class ekliyor ve _formbuilder.scss sticky barı dialog kenarına yapıştırıp rounded-b-xl ile alt köşelerini Dialog'un yuvarlatmasına eşliyor. AvatarUpload dikey kart düzeninden tek satır row layout'a (avatar · başlık/hint · butonlar) geçirildi; 56px küçültülmüş avatar, primary border vurgusu ve yeni initials prop'u eklendi. title ve subtitle prop'larının davranışı netleştirildi: verilmezse default i18n, dolu string → o metin, boş string '' → satır tamamen gizlenir (eski geçici davranış yeniden bozmadan eski API uyumu sağlanıyor). Tipografi 14px root tabanlı rem sistemine geri çevrildi (kullanıcı tarayıcı font-size ayarları ve a11y zoom orantılı çalışıyor); önceki geçici mutlak-px override'ı kaldırıldı. Profil dikey sekmelerine description metni ve sekme başına ikon rengi eklendi.

Eklendi

  • Profil sekmeleri — Profile/Index.vue artık her tab için description() ve iconColor() çağırıyor; sk-profile.tab_descriptions.{general,password,security,sessions} i18n key'leri tanıtıldı (TR/EN).
  • AvatarUpload :initials — avatarUrl yokken avatar kutusunda kullanıcının baş harflerini gösterir; verilmezse mevcut pi-user fallback'i korunur.

Değiştirildi

  • AvatarUpload row layout — avatar size-14 boyutuna küçültüldü, primary-200 border + primary-50 zemin, "Kaldır" severity-secondary text, "Değiştir" outlined. Başlık ve hint inline basılır; :title="" ve/veya :subtitle="" ile başlık bloğu komple gizlenebilir.
  • AvatarUpload title / subtitle semantiği — undefined → default i18n key, dolu string → birebir metin, '' → element v-if ile gizli. Etiketleri tamamen kapatma yetkisi geri geldi.
  • sk-avatar.hint — metni teknik formata güncellendi: "JPG · PNG · GIF — en fazla 2 MB · 512×512 önerilir" (EN: "JPG · PNG · GIF — max 2 MB · 512×512 recommended").
  • Tipografi (14px root, rem) — _base.scss artık html { font-size: 0.875rem } ile root'u 14px'e sabitliyor (browser default 16px'ten ölçeklenir); utilities.css tüm --text-* token'larını bu root'a göre rem cinsinden tanımlıyor (--text-base: 1rem, --text-xs: 0.857rem vb.). Geçici mutlak-px override'ı kaldırıldı, a11y zoom yine orantılı çalışıyor.
  • FileManager metin dengeleme — favoriler/çöp boş durum başlık/altyazıları ve dosya tipi filtre pill'leri text-lg → text-base. sk-user-menu__item ise text-sm → text-base ile büyütüldü, yeni base ile hizalandı.

Düzeltildi

  • Sticky action bar sızıntısı (AppDialog/SkForm) — Dialog içindeki uzun formlar, sticky alt barın altından scroll içeriği akıtıyordu. Çözüm üç parçalı:
    1. AppDialog.vue — Dialog content PT'sine padding-bottom: 0 + flex column layout
    2. SkForm.vue — dialog mode'da sk-fb--dialog marker class
    3. _formbuilder.scss — .sk-fb__actions opak var(--p-content-background) zemin; dialog mode'da -mx-5 px-5 ile edge-to-edge, alt köşeler rounded-b-xl ile Dialog'un borderRadius.xl'sine eşitlendi.

Yükseltme

composer update lvntr/laravel-starter-kit

# Etkilenen stub'ları yeniden yayınla (DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın)
# stubs/resources/css/theme/{_base.scss,utilities.css,_formbuilder.scss,_tabs.scss,_menus.scss}
# stubs/resources/js/pages/Profile/Index.vue
# stubs/resources/js/pages/Profile/components/ProfileInfoTab.vue
# stubs/lang/{tr,en}/{sk-avatar.php,sk-profile.php}
php artisan vendor:publish --tag=starter-kit-stubs --force

AvatarUpload davranış notu: Eğer kodunuzda :subtitle="" geçip default hint'in yine de basılmasını bekliyorduysanız, artık '' hint satırını gizler. Default i18n davranışını geri getirmek için prop'u tamamen kaldırın veya dolu bir string verin.


2026-05-10 — v13.5.6

Yama sürüm — SystemHealthTab'dan axios kaldırıldı, API envelope uyumu, FileManager tip düzeltmesi

SystemHealthController, zorunlu to_api() yardımcısı yerine response()->json() kullanıyordu; bu, useApi composable'ının parse edemediği standart dışı bir JSON gövdesi (success envelope yok) üretiyordu. Düzeltildi. SystemHealthTab.vue, tüm API çağrılarının useApi composable üzerinden yapılması gerektiği SK kuralını ihlal ederek doğrudan axios import edip çağırıyordu; useApi({ toast: false }) ile değiştirildi. FileManager.vue'daki TypeScript hatası giderildi: busy tipi BusyState | null ve vue-tsc, event handler içinde v-if üzerinden daraltma yapamıyor; çift isteğe bağlı zincirleme busy?.onCancel?.() her iki null durumunu çözüyor.

Düzeltmeler

  • SystemHealthController@run — response()->json() → to_api([...], $message) ile değiştirildi; return tipi ApiResponse|RedirectResponse olarak güncellendi. Ham JSON yanıtı, useApi'nin beklediği standart { success, data, message } envelope'unu atlıyordu ve frontend'de parse hatasına neden oluyordu.
  • SystemHealthTab.vue — import axios from 'axios' kaldırıldı; useApi({ toast: false }) composable eklendi. axios.post<...>(url) → api.post<...>(url) ile değiştirildi. Axios'u doğrudan kullanmak SK kuralını ihlal ediyor; tüm API çağrıları useApi üzerinden yapılmalı.
  • FileManager.vue — @click="busy.onCancel" → @click="() => busy?.onCancel?.()" olarak düzeltildi. busy tipi BusyState | null, onCancel tipi (() => void) | null; vue-tsc event handler içinde v-if üzerinden hiçbirini daraltamıyor; çift isteğe bağlı zincirleme gerekli.

Yükseltme

composer update lvntr/laravel-starter-kit

# Etkilenen stub'ları yeniden yayınla (DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın)
# SystemHealthController.php, SystemHealthTab.vue
php artisan vendor:publish --tag=starter-kit-stubs --force

2026-05-07 — v13.5.5

Yama sürüm — System Health Settings'e taşındı, paylaşım iptali UUID düzeltmesi, kurulum güvenilirliği

System Health artık bağımsız bir admin sayfası değil; içeriği SystemHealthTab.vue içinde PrimeVue Card ile sarılmış bir Settings sekmesi olarak sunuluyor. Admin kenar menüsü girişi kaldırıldı. Bir migration hatası düzeltildi: file_manager_share_revocations tablosundaki revoked_by_user_id kolonu unsignedBigInteger olarak tanımlanmıştı ancak users tablosu UUID primary key kullanıyor; kolon artık doğru şekilde uuid olarak beyan ediliyor. InstallCommand'a composer dump-autoload çalışmadan önce app/Helpers/custom.php dosyasını otomatik oluşturan bir guard eklendi; bu dosyanın yokluğu temiz kurulumda her artisan çağrısını kırıyordu. ApiClientsManageTab ve ApiTokensManageTab stub'ları el yapımı <header> bloklarını DatatableBuilder kart API'si (isCard, cardTitle, cardSubtitle, create()) lehine bıraktı. SkDatatable kart başlığı artık doğru body padding'ine sahip; başlık ve altyazı standart Card body ritmiyle hizalanıyor.

Değişiklikler

  • System Health Settings sekmesine taşındı. /admin/system-health bağımsız sayfası, bir Settings sekmesiyle değiştirildi. useAdminMenu.ts'teki kenar menüsü girişi ve system-health route import'u kaldırıldı. SystemHealthTab.vue artık PrimeVue Card içinde title, subtitle ve content slot'larıyla sarılı; yenile butonu #title slot'una size="small" ile yerleştirildi.
  • SystemHealthController@run — back() kullanımına geri döndürüldü. v13.5.4'te eklenen redirect()->route('admin.system-health.index'), System Health artık Settings sayfasının içinde olduğu için anlamsız hale geldi.
  • ApiClientsManageTab.vue / ApiTokensManageTab.vue — özel <header> bloğu ve bağımsız Button import'u, tablo builder üzerindeki isCard(true).cardTitle(...).cardSubtitle(...) ile değiştirildi; create action tableBuilder.create({ label, onClick }) ile kaydediliyor, tam kart düzeni artık DatatableBuilder tarafından yönetiliyor.

Düzeltmeler

  • file_manager_share_revocations migration — revoked_by_user_id kolonu, users tablosundaki UUID primary key ile uyumlu olması için unsignedBigInteger'dan uuid'e değiştirildi. v13.5.3'ten yükseltiyorsanız aşağıdaki migration notuna bakın.
  • ShareRevocation modeli — $revoked_by_user_id PHPDoc tipi int|null'dan string|null'a düzeltildi.
  • InstallCommand — app/Helpers/custom.php yoksa composer dump-autoload'dan önce otomatik oluşturuluyor (minimal <?php stub). Bu dosyanın yokluğu temiz kurulumda sonraki her artisan çağrısını kırıyordu.
  • DatabaseTestCase — bellek içi file_manager_share_revocations şeması, düzeltilen migration ile uyumlu olması için revoked_by_user_id alanında uuid kullanacak şekilde güncellendi.

UI

  • SkDatatable — isCard modunda caption PT slot'u artık padding: var(--p-card-body-padding) var(--p-card-body-padding) 0 alıyor; başlık ve altyazı standart Card body hizalamasına oturuyor, tablo araç çubuğu ve içerik tam genişlikte kalmaya devam ediyor.

Yükseltme

composer update lvntr/laravel-starter-kit

# Etkilenen stub'ları yeniden yayınla (DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın)
# useAdminMenu.ts, SystemHealthController.php, SystemHealthTab.vue,
# ApiClientsManageTab.vue, ApiTokensManageTab.vue
php artisan vendor:publish --tag=starter-kit-stubs --force

Migration notu — file_manager_share_revocations tablosunu v13.5.3'te yayınladıysanız kolon tipini düzeltmek için yeni bir migration çalıştırın:

Schema::table('file_manager_share_revocations', function (Blueprint $table) {
    $table->dropForeign(['revoked_by_user_id']);
    $table->dropColumn('revoked_by_user_id');
    $table->uuid('revoked_by_user_id')->nullable()->after('revoked_at');
    $table->foreign('revoked_by_user_id')->references('id')->on('users')->nullOnDelete();
});

Bu sürümde yeni izin, config anahtarı veya davranış değişikliği bulunmuyor.


2026-05-07 — v13.5.4

Yama sürüm — v13.5.3 sonrası stub düzeltmeleri, tip uyumlulukları ve CI pipeline iyileştirmeleri

Bu yama sürüm, v13.5.3 sonrası ortaya çıkan stub regresyonlarını gideriyor: AdminHeader'da role yazım hatası, eksik System Health menü item'ı, SettingsDefaultsQuery'de storage_usage payload eksikliği, SystemHealthController'da yanlış redirect hedefi ve Logs sayfalarındaki trans() count tipi sorunları. TabBuilder rose icon rengini kazandı (System Health sekmesinin derlenmesi için gerekli). CI pipeline'ı yeniden sıralandı: auto-imports.d.ts ve components.d.ts artık typecheck'ten önce üretiliyor; vite.config.ts'e PHP olmayan ortamlar (Wayfinder) ve Vitest çalıştırması (laravel-vite-plugin HMR kontrolü) için yeni guard'lar eklendi. Yeni izin, migration veya config anahtarı yok.

Eklenenler

  • TabBuilder — rose icon rengi. TabIconColor artık rose'u kabul ediyor; _tabs.scss'e karşılık gelen --p-rose-* light/dark kuralları eklendi. Settings'teki System Health sekmesi bunu kullanıyor.

Düzeltmeler

  • AdminHeader.vue — page.props.auth?.role (singular, var olmayan) roles?.[0]'a düzeltildi; shared page-prop roles: string[] shape'iyle uyumlu.
  • useAdminMenu.ts — eksik import systemHealth from '@/routes/system-health' ve System Health menü item'ı (permission: 'system.health.view') eklendi. v13.5.3 sayfasına yalnızca URL üzerinden erişilebiliyordu.
  • SettingsDefaultsQuery.php — storage_usage (used_bytes, quota_bytes) payload'ı ResolvesMediaModel trait'i (computeStorageUsed() / storageQuotaBytes()) üzerinden eklendi. v13.5.2'de gelen StorageQuotaCard'ı besliyor.
  • SystemHealthController@run — back() yerine redirect()->route('admin.system-health.index') (POST → güvenli GET).
  • Admin/Logs/{Index,Show}.vue — trans()/$t() count parametreleri String(...) ile sarmalandı; laravel-vue-i18n v2.8 strict tipi number değer kabul etmiyordu.
  • tsconfig.json — @lvntr/components/* path mapping Vite alias'ıyla hizalandı; @lvntr/components/FormBuilder/core gibi yollar artık vue-tsc altında çözümleniyor.
  • env.d.ts — window.turnstile için tipli global Window genişletmesi ve @/routes/* wildcard module declaration (wayfinder dosyaları henüz üretilmediğinde fallback) eklendi.

Build / CI

  • vite.config.ts — isWayfinderAvailable() artisan yoksa wayfinder plugin'ini atlar (CI / paket reposu); isVitest guard'ı vitest run sırasında laravel-vite-plugin ve inertia()'yı atlar (CI'da "Vite HMR server" startup hatası giderildi).
  • GitHub Actions Node job yeniden sıralandı — npm ci → vendor symlink → route stub generation → build → typecheck → lint (continue-on-error) → test. Build artık auto-imports.d.ts ve components.d.ts'i vue-tsc çalışmadan önce üretiyor.
  • scripts/ci/generate-route-stubs.mjs — node-only CI fallback'i; 16 @/routes/* modülü için minimal stub dosyaları yazar. Çıktı dizini gitignore'lı; host app'lerde wayfinder gerçek dosyaları üretmeye devam ediyor.
  • Doctor testleri — kasıtlı olarak İngilizce kalan check mesajları için beklentiler güncellendi.
  • .gitignore — wayfinder routes dizini, CI vendor symlink ve Vite build artifact'ları (stubs/public/build/, stubs/bootstrap/ssr/) eklendi.

Yükseltme

composer update lvntr/laravel-starter-kit

# Etkilenen stub'ları yeniden yayınla (DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın)
# AdminHeader.vue, useAdminMenu.ts, SystemHealthController.php, SettingsDefaultsQuery.php,
# Logs/{Index,Show}.vue, env.d.ts, tsconfig.json, vite.config.ts
php artisan vendor:publish --tag=starter-kit-stubs --force

# Yeni rose tab color CSS'ini içeren tema dosyalarını yayınla
php artisan vendor:publish --tag=starter-kit-theme --force

npm run build

Özelleştirilmiş bir tsconfig.json kullanıyorsanız @lvntr/components/* mapping'ini elle ekleyin (@lvntr/*'dan önce gelmeli):

"paths": {
    "@/*": ["resources/js/*"],
    "@lvntr/components/*": [
        "vendor/lvntr/laravel-starter-kit/resources/js/components/Lvntr-Starter-Kit/*"
    ],
    "@lvntr/*": ["vendor/lvntr/laravel-starter-kit/resources/js/*"]
}

Yeni izin, migration veya config anahtarı yok.


2026-05-06 — v13.5.3

Bu sürüm sk:doctor sağlık kontrol komutunu ve System Health admin sayfasını, HMAC imzalı dosya paylaşım bağlantılarını, DatatableBuilder için cross-page Bulk Action API'sini, Domain Generator v2 opt-in flag'lerini ve tam Passport API Client & Token admin arayüzünü ekliyor. Güvenlik bağımlılık güncellemeleri, iç içe klasör silme için event-dispatch düzeltmeleri, bulk controller'lar için Inertia flash response düzeltmeleri ve UUID/ULID bulk-action ID desteği de bu sürüme dahildir. Mevcut uygulamalar aşağıdaki yükseltme adımlarını uygulamalı.

Eklenenler

  • sk:doctor artisan komutu — 12 kontrol noktasını kapsayan sistem sağlık denetimi: PHP extension'ları, veritabanı bağlantısı, Redis, Passport anahtarları, storage symlink, yazılabilir dizinler, queue driver, schedule çalışması, mail driver, npm build artifact'ları, config cache, FileManager disk bağlantısı. --json ile makine okunabilir çıktı; --only=database,redis,... ile seçili kontroller çalıştırılabilir. Exit kodları: 0 OK, 1 WARN, 2 FAIL.
  • Admin Panel — System Health sayfası (/admin/system-health) — sk:doctor çıktısını UI'da görselleştirir; kontrol başına durum rozeti ve manuel yenile butonu. Erişim izni: system.health.view.
  • File Manager — Signed Share Link — HMAC imzalı genel erişim URL'leri. POST /file-manager/share ile TTL belirterek paylaşım oluşturulur; POST /file-manager/share/revoke ile iptal edilir; GET /file-manager/share/{media}?expires&signature ile doğrulama yapılır. Config anahtarları: file-manager.share.enabled, default_ttl_hours (varsayılan 24), max_ttl_hours (varsayılan 720), allow_revoke. Token iptali file_manager_share_revocations tablosunda (media_id, signed_token_hash) composite unique index ile yönetilir. Yeni izinler: share-media, revoke-share-media.
  • DatatableBuilder — Bulk Action API — BulkAction interface ve BulkActionDispatcher ile sayfa sınırını aşan toplu işlem desteği. SkDatatable, select_all_filtered modunu (filtre snapshot ile) ve cross-page seçimi destekler. Request payload: {action, ids, select_all_filtered, filter_snapshot}; response: {processed, skipped, failed, message}. Stub örnekleri: BulkDeleteUserAction (rank-aware) ve BulkDeleteRoleAction (sistem rollerine karşı koruma).
  • Domain Generator v2 (make:sk-domain) — opt-in flag'ler — --with-policy, --with-factory, --with-seeder, --with-test, --with-relations tek tek ya da --with=policy,factory,test toplu syntax ile kullanılabilir. --relations="belongsTo:User,hasMany:Comment,morphTo:commentable" ile ilişki scaffold'ı otomatik üretilir. Flag'siz çağrım v13.5.x davranışını korur (geriye dönük uyumlu).
  • API Client & Token Admin UI — Passport authorization_code ve client_credentials grant'leri ile Personal Access Token yönetimi için admin arayüzü (/admin/api-clients, /admin/api-tokens). Client secret ve PAT plaintext yalnızca oluşturma response'unda bir kez gösterilir (Cache-Control: no-store); OneTimeSecretModal dismiss edilemez. Yeni izinler: api-clients.create, api-clients.read, api-clients.update, api-clients.delete, api-tokens.create, api-tokens.read, api-tokens.delete. Yeni validation rule: HttpsOrLocalhostUrl (RFC 8252 §8.3 — yalnızca HTTPS, localhost istisnası ile HTTP).
  • CI Workflow (GitHub Actions) — PHP test (pest), lint (pint), Node 22 build/typecheck/lint job'ları. Aynı branch/PR'da eş zamanlı çalışan job'lar concurrency: cancel-in-progress ile iptal edilir.
  • composer test (vendor/bin/pest tests/Feature) ve composer lint (vendor/bin/pint --test) script'leri katkıda bulunanlar için eklendi.

Düzeltmeler

  • DeleteFolderAction — alt klasörler, Eloquent model event'lerini atlayan query-builder forceDelete() çağrısıyla kalıcı siliniyordu. FileFolder modelindeki forceDeleted gözlemcisi (favori kayıtlarını temizlemekten sorumlu) alt klasörler için hiç tetiklenmiyordu; bu da file_favorites tablosunda sahipsiz kayıtlar bırakıyordu. Model bazlı iterasyona geçildi, artık her forceDeleted event'i doğru şekilde tetikleniyor.
  • sk:update — node_modules/ stubs taramasından filtrelendi. NEVER_UPDATE_PATHS sabitine node_modules/ eklendi; isNeverUpdate() kontrolü updateModifiableFiles, addNewFiles, migrateHashRegistry ve updateHashRegistry döngülerinin tamamına uygulandı. Sembolik link (path repository) ortamında stubs/node_modules/ varlığı aday dosya listesine sızıyordu.
  • sk:doctor ve sk:update console çıktısı İngilizceye çevrildi. DoctorCommand, UpdateCommand ve 12 DoctorCheck sınıfındaki tüm kullanıcıya gösterilen mesajlar, ipuçları ve tablo başlıkları İngilizce; PHP kod yorumları değiştirilmedi.
  • Bulk action controller'lar — Inertia flash response. UserBulkController ve RoleBulkController artık ApiResponse (JSON) yerine back()->with('success'/'error', ...) döndürüyor. Önceki JSON response Inertia'nın onSuccess/onError akışını kırıyor, ham JSON'u ekrana basıyordu; başarı/hata mesajları artık HandleInertiaRequests flash paylaşımı üzerinden SkFlash/useFlash bileşenine ulaşıyor.
  • Bulk action validasyonu — UUID/ULID/integer ID desteği. BulkActionRequest::rules() güncellendi: ids.* kuralı integer'dan string|min:1|max:64'e değiştirildi; prepareForValidation() tüm ID'leri string'e cast ediyor. Önceki integer kuralı HasUuids kullanan modellerde (User, FileBucket, FileFolder vb.) "The ids.0 field must be an integer" hatasına yol açıyordu. Yeni kural integer auto-increment, UUID (36 karakter) ve ULID (26 karakter) primary key'leri tek payload şemasında destekler.

Güvenlik

  • dedoc/scramble ^0.13'ten ^0.13.22'ye yükseltildi — v0.13.22'de giderilen RCE sınıfı advisory (GHSA) için.
  • phpseclib/phpseclib 3.0.51'den 3.0.52'ye güncellendi — laravel/passport üzerinden gelen yüksek önem dereceli DoS advisory için.
  • Signed Share Link — cross-media token hijack koruması. (media_id, signed_token_hash) composite unique index, bir token'ın farklı media kayıtlarında geçerli sayılmasını engeller.
  • Personal Access Token — privilege escalation guard. user_id body alanı kabul edilmez; token her zaman kimliği doğrulanmış kullanıcı için mint edilir.
  • Passport client confidential zorunluluğu. API Client UI üzerinden yalnızca confidential=true client oluşturulabilir; authorization_code grant için min:1 redirect URI ve HTTPS zorunlu. Mevcut DB kayıtları etkilenmez.

Değişiklikler

  • StarterKitServiceProvider — Passport scope ve Gate::before kayıtları tek kaynak haline getirildi; AppServiceProvider stub'ından duplicate kayıtlar kaldırıldı.

Yükseltme

composer update lvntr/laravel-starter-kit

# Yeni migration'ları yayınla ve çalıştır
php artisan vendor:publish --tag=starter-kit-migrations
php artisan migrate

# Yeni share.* anahtarlarını içeren file-manager config'ini yayınla
php artisan vendor:publish --tag=starter-kit-config --force

# Yeni admin sayfa ve controller stub'larını yayınla
# DİKKAT: özelleştirilmiş stub'lar override edilir — önce diff alın
php artisan vendor:publish --tag=starter-kit-stubs --force

# Yeni izinleri ekle ve permission cache'ini temizle
php artisan db:seed --class=PermissionResourcesSeeder
php artisan permission:cache-reset

Yeni izinler: system.health.view, share-media, revoke-share-media, api-clients.create, api-clients.read, api-clients.update, api-clients.delete, api-tokens.create, api-tokens.read, api-tokens.delete.

Davranış değişiklikleri:

  • confidential=false ile authorization_code Passport client'ları UI üzerinden artık oluşturulamaz. Mevcut DB kayıtları etkilenmez.
  • Personal Access Token mint: user_id body alanı kaldırıldı; admin başka kullanıcı adına PAT oluşturmak istiyorsa artisan komutu veya özel action kullanılmalıdır.
  • AppServiceProvider stub'ında duplicate Passport scope / Gate::before bloğu varsa silinmeli; StarterKitServiceProvider üzerinden çalışmaya devam eder.

2026-05-06 — v13.5.2

Yama sürüm — Ayarlar güvenlik sekmesi birleştirmesi, FileManager geri yükleme düzeltmesi ve i18n iyileştirmeleri

Ayarlar paneli Kimlik Doğrulama ve Turnstile sekmelerini tek Güvenlik sekmesinde birleştirdi; disk kullanımını görselleştiren bir Depolama Kotası kartı eklendi. File Manager çöp kutusu geri yükleme hatası giderildi: çöp kutusu artık yalnızca kök seviyedeki silinmiş öğeleri gösteriyor, böylece tekil ve toplu geri yükleme işlemleri "parent in trash" hatasıyla karşılaşmıyor. Tüm File Manager bileşenlerindeki metin boyutları text-lg (14 px) olarak standardize edildi, onay diyalogları trans() üzerinden çevrildi ve filtre pill etiketleri i18n'e geçirildi. Mevcut uygulamalar composer update lvntr/laravel-starter-kit && php artisan sk:update && npm run build çalıştırmalı.

Added

  • SecurityTab.vue Kimlik doğrulama ve Cloudflare Turnstile ayarlarını tek sekmede birleştirir; kaldırılan AuthTab.vue ve TurnstileTab.vue stub'larının yerini alır.
  • StorageQuotaCard.vue Ayarlar panelinde disk-genel depolama kotası kullanımını progress bar ile gösterir.
  • SettingsDefaultsQuery artık Inertia payload'ında storage_usage (used_bytes, quota_bytes) döndürür.
  • i18n key'leri eklendi — sk-setting (güvenlik/depolama bölüm etiketleri), sk-file-manager (filtre pill etiketleri: all, image, video, pdf, audio, archive) ve sk-common (onay diyalog string'leri).
  • config('file-manager.settings.enable_trash') — FileManager genelinde soft-delete vs hard-delete davranışını kontrol eden yeni config key'i. true (varsayılan) silinen dosya ve klasörleri Çöp Kutusu'na gönderir; false anında kalıcı olarak siler. DeleteFileAction ve DeleteFolderAction silme anında bu config'i okur. Değer Inertia üzerinden otomatik paylaşılır (fileManagerSettings.enable_trash); Vue bileşeni :enable-trash prop'u verilmediğinde config değerine geri döner. Prop yine de instance bazında override için geçilebilir.

Fixed

  • Çöp kutusu geri yükleme hatası. TrashContentsQuery artık yalnızca kök seviyedeki silinmiş öğeleri döndürüyor. Üst klasörü de çöp kutusunda olan öğeler bağımsız öğe olarak listeleniyordu ve tekil/toplu geri yüklemede "Cannot restore: the parent folder is also in trash" hatasına yol açıyordu. Kök filtresiyle geri yükleme işlemleri her zaman ağacın tepesinden başlar.

Changed

  • FileManager minimum metin boyutu text-lg (14 px) olarak FileManager.vue, FileGrid.vue, FileManagerSidebar.vue ve FileManagerStats.vue genelinde standardize edildi.
  • useConfirm composable — onay diyalog string'leri yeni sk-common çeviri key'lerini kullanan trans() çağrılarına taşındı.
  • Admin/Files/Index.vue sadeleştirildi — gereksiz sarıcı <div> kaldırıldı.
  • File Manager sekmesi — Video/Audio yükleme toggle'ları artık görsel açıdan Images ile aynı checkbox-grid tasarımını kullanıyor.

Removed

  • AuthTab.vue ve TurnstileTab.vue stub'ları — içerik SecurityTab.vue'a taşındı. sk:update, DEPRECATED_PATHS üzerinden otomatik temizler.

Upgrade

composer update lvntr/laravel-starter-kit
php artisan sk:update
npm run build

2026-05-05 — v13.5.1

Yama sürüm — NPM exports düzeltmesi, sk:publish iyileştirmeleri, depolama kotası ve yükleme validasyonu

NPM paketi main ve exports path'leri gerçek dosya yapısıyla eşleştirildi. sk:publish bireysel tag'leri artık doğru çalışıyor. Admin Settings > File Manager panelinden GB cinsinden depolama kotası ayarlanabilir; kota aşıldığında yükleme istekleri yerelleştirilmiş hata ile 422 döndürür. Mevcut uygulamalar composer update lvntr/laravel-starter-kit && php artisan sk:update && npm install && npm run build çalıştırmalı.

Fixed

  • NPM paketi main ve exports path'leri artık gerçek dosya yapısını yansıtıyor (resources/js/components/Lvntr-Starter-Kit/...). FileManager export'u eklendi.
  • sk:publish bireysel tag'leri (form, datatable, tabs, skeleton, ui) eski yapıya göre kırık source path'lere sahipti; Lvntr-Starter-Kit/ segment'i ile düzeltildi.
  • vendor:publish --tag=starter-kit-components iç içe path bug'ı giderildi. Önceki: resources/js/components/Lvntr-Starter-Kit/Lvntr-Starter-Kit/.... Şimdi: doğrudan resources/js/components/Lvntr-Starter-Kit/.
  • vendor:publish --tag=starter-kit-file-manager-components artık aktif. Source path eski dizin adına işaret ediyordu (file-manager); gerçek dizin yapısıyla (Lvntr-Starter-Kit/FileManager) hizalandı.
  • index.ts barrel'da eksik 9 component export eklendi: EditorInput, EditorImagePicker, EditorColorPalette, TranslatableInput, ImageLightbox, FilePreviewModal, ToggleFeatureCard, MimePickerField, SkTag.

Added

  • sk:publish --tag=filemanager — FileManager UI'ını ayrıca yayınlamak için yeni tag.
  • sk:install --without-ai-skill — Claude Code skill bundle kullanmayan consumer'lar için stubs/.claude/skills/ yayınını atla.
  • .gitattributes — Composer arşivi artık tests/, docs/, .github/, plan-docs/, package-audit-notes/ vb. geliştirme dosyalarını dışlıyor; arşiv boyutu küçüldü.
  • .npmignore — NPM paketi __tests__/, *.spec.*, *.test.* dosyalarını dışlıyor (root ve alt dizinler; npm 11 davranışıyla uyumlu).
  • Disk-genel depolama kotası (storage_quota_gb). Admin Settings > File Manager panelinden GB cinsinden tek kota değeri tanımlanır (varsayılan 10 GB). Tüm context'leri (user, global, özel morph map girişleri) ve çöp kutusunu (withTrashed) kapsar.
  • Upload kota validasyonu. UploadFileRequest::withValidator() kota kontrolü ekler; kota aşıldığında HTTP 422 ve yerelleştirilmiş errors.quota_exceeded mesajı döner.

Removed

  • Stub'dan duplike vendor-owned domain command'ları silindi: EnvSyncCommand, MakeDomainCommand, RemoveDomainCommand. Vendor'dan tek kaynak olarak çalışmaya devam eder. sk:update mevcut consumer projelerde DEPRECATED_PATHS ile otomatik temizler.
  • App\Http\Responses\ApiResponse.php stub'ı silindi. StarterKitServiceProvider alias guard'ı (App\Http\Responses\ApiResponse → Lvntr\StarterKit\Http\Responses\ApiResponse) consumer dosyası silindikten sonra otomatik devreye girer; mevcut use App\Http\Responses\ApiResponse; import'ları değişmeden çalışır.
  • Lvntr\StarterKit\Enums\PermissionEnum vendor'dan silindi. Resmi konum App\Enums\PermissionEnum (stubs altında). Vendor'da referans yoktu (grep onayladı). Kodunuz doğrudan bu namespace'i import ediyorsa App\Enums\PermissionEnum'a güncelleyin.

Changed

  • sk:publish primary publish komutu olarak konumlandırıldı. Granular interactive flow ve namespace rewrite desteği. vendor:publish --tag=starter-kit-* BC için korunur; sk:publish artık install ve komut dokümantasyonunda öne çıkar.
  • ResolvesMediaModel::computeStorageUsed() imzası değişti (internal trait). Parametre almaz hale geldi; Media::withTrashed()->sum('size') ile disk-genel toplam döndürür. Önceki davranış model_type + model_id filtreli per-context hesaplamaydı. Bu trait'i extend edip computeStorageUsed($context) çağırıyorsanız parametreyi kaldırın.
  • FolderContentsQuery, FavoritesContentsQuery, TrashContentsQuery — stats.storage_quota alanı byte cinsinden eklendi.
  • FileManager.vue — STORAGE_QUOTA_BYTES hardcoded sabiti kaldırıldı; quotaBytes computed değeri stats.storage_quota'dan okunuyor. Kota sıfır veya tanımsızsa sidebar v-if="quotaBytes > 0" ile gizlenir.

Upgrade

composer update lvntr/laravel-starter-kit
php artisan sk:update

sk:update çıktısında "Removed" listesinde 4 path görünecek — bu beklenen davranış.


2026-05-05 — v13.5.0

Major sürüm — Vendor-first runtime ve frontend UI lib taşıması

Starter kit runtime tamamen vendor'a taşındı. FileManager backend, paylaşılan base sınıflar, trait'ler, helper'lar, middleware'ler, ApiResponse ve route loader artık vendor/lvntr/laravel-starter-kit/src/ altında Lvntr\StarterKit\ namespace'iyle çalışıyor. Frontend bileşen kütüphanesi (DatatableBuilder, FormBuilder, TabBuilder, FileManager, Skeleton, ui) de artık paketin canonical konumunda, app tarafı vendor symlink üzerinden tüketiyor. Mevcut uygulamalar yalnızca composer update çalıştırmalı; hiçbir dosya değişmez, rota adı kırılmaz, php artisan migrate "Nothing to migrate" döner. Frontend geçişi tamamen isteğe bağlıdır. Yükseltme talimatları: UPGRADE.md.

Changed

  • Vendor-first yapıya geçildi. Paket runtime artık stub akışından değil, doğrudan vendor/ altından çalışıyor. sk:install iskelet dosyalarını (auth, layout, user/rol/ayar domain, config) publish eder; FileManager ve Shared katmanlarını app/ dizinine kopyalamaz.
  • sk:update basitleştirildi. Vendor runtime için kopyalama yapmıyor; composer update yeterli. Hash takipli stub'lar (auth/layout/user/rol/ayar) için mevcut davranış korundu.
  • Frontend UI lib taşındı. resources/js/components/Lvntr-Starter-Kit/{DatatableBuilder,FormBuilder,TabBuilder,FileManager,Skeleton,ui,index.ts} artık paketin canonical konumudur. App tarafı vendor symlink üzerinden tüketir.
  • stubs/vite.config.ts alias güncellendi. Yeni install için @lvntr/components alias'ı vendor/lvntr/laravel-starter-kit/resources/js/components/Lvntr-Starter-Kit path'ini kullanır; preserveSymlinks: true; Components({ dirs }) array'inde vendor path mevcut.
  • FileManagerAction abstract base + ResolvesMediaModel trait. media-library.media_model config'i üzerinden Media model resolve eder. App-specific App\Models\Media overrider'ları (örn. SoftDeletes ile) backward compatible çalışır.
  • Http/Requests/FileManager/UploadFileRequest. Protected method'lar — app tarafında override edilebilir (Setting entegrasyonu vb.).

Added

  • src/Domain/FileManager/ — Actions, DTOs, Queries, Services, Support Lvntr\StarterKit\Domain\FileManager\ namespace'iyle vendor'da.
  • src/Domain/Shared/ — BaseAction, BaseDTO, ActionPipeline, PipeableAction Lvntr\StarterKit\Domain\Shared\ namespace'iyle vendor'da.
  • src/Traits/ — HasActivityLogging, HasMediaCollections Lvntr\StarterKit\Traits\ namespace'iyle vendor'da.
  • src/sk-helpers.php — to_api(), definition(), definitionLabel(), sk_locale_keys(), sk_default_locale(), format_date() fonksiyonları function_exists guard'larıyla vendor'da.
  • src/Http/Responses/ApiResponse.php — {success, status, message, data, errors?} envelope formatı korunarak vendor'a taşındı.
  • src/Http/Middleware/ — CheckResourcePermission, SecurityHeaders Lvntr\StarterKit\Http\Middleware\ namespace'iyle vendor'da.
  • src/Http/Controllers/FileManagerController.php ve src/Http/Requests/FileManager/* — vendor'da.
  • src/Console/Commands/PurgeFileManagerTrashCommand.php — file-manager:purge-trash signature AYNEN korundu.
  • src/Exceptions/ — ApiException, ApiExceptionHandler vendor'da.
  • src/Facades/FileManager.php — FileManager::routes() ile tek satır route mount.
  • src/routes/file-manager.php — 19 route, isimler AYNEN. Consumer'ın kendi route dosyası varsa vendor mount edilmez.
  • database/migrations/ — 3 FileManager migration, dosya adı ve içerik AYNEN korundu.
  • config/file-manager.php — models.* ve settings.* key'leri eklendi.

Deprecated

  • sk:sync (PackageSyncCommand). Composer path symlink workflow'unda gereksiz hale geldi. --force ile escape hatch korunur.

Upgrade

Güncelleme sonrası yeterli:

composer update lvntr/laravel-starter-kit
php artisan migrate

Mevcut app/Domain/FileManager/, app/Domain/Shared/, app/Traits/, app/Helpers/sk-helpers.php gibi dosyalar yerinde kalır ve çalışmaya devam eder. Bu dosyaları vendor versiyonuyla değiştirmek tamamen isteğe bağlıdır. Frontend cleanup (Vite alias'ını vendor path'e yönlendirme ve app tarafındaki kopyayı silme) da opt-in'dir. Her iki rehber için bkz. UPGRADE.md.


2026-05-04 — v13.4.10

Minor sürüm — Çevrilebilir FormBuilder alanları ve Sample Contents referans modülü

FormBuilder artık çok dilli metin alanlarını kutudan çıktığı gibi destekliyor. Üç yeni builder — FB.translatableText(), FB.translatableTextarea() ve FB.translatableEditor() — aktif her dil için ayrı input render eder ve Spatie Translatable modelleriyle uyumlu JSON locale map'i submit eder. Bu sürüm ayrıca validation, datatable arama/sıralama ve resource çıktısı için backend helper'ları ve tüm pattern'i uçtan uca gösteren Sample Contents modülünü ekler. Mevcut uygulamalar composer update lvntr/laravel-starter-kit && php artisan sk:update && php artisan migrate && npm install && npm run build çalıştırmalı.

Added

  • Çevrilebilir FormBuilder alanları. FB.translatableText(), FB.translatableTextarea() ve FB.translatableEditor(), aktif dil listesine göre locale bazlı input'lar render eder. Locale filtreleme (onlyLocales, exceptLocales), inline/tab layout ve locale label stilleri (badge, name, flag) desteklenir.
  • Backend translatable helper'ları. HasTranslatableRules, FormRequest kurallarını ve validation label'larını locale bazında üretir. TranslatableQueryHelpers, JSON kolon araması, locale-aware sıralama ve datatable/edit form için resourceShape() çıktısı sağlar.
  • Locale helper fonksiyonları. sk_locale_keys() aktif locale kodlarını sırayla döndürür; sk_default_locale() primary locale'i çözer ve gerekirse app.fallback_locale değerine düşer.
  • Sample Contents modülü. Translatable model, migration, factory, domain action/event/listener'ları, FormRequest'ler, resource, datatable query, Vue sayfaları ve menü/yetki kayıtlarıyla tam bir admin CRUD referansı gelir.
  • Dokümantasyon. Yeni Translatable Fields ve Çevrilebilir Alanlar rehberleri backend/frontend akışını, migration stratejisini ve Sample Contents referans implementasyonunu anlatır.
  • Paket bağımlılığı. JSON tabanlı çevrilebilir attribute'lar için spatie/laravel-translatable artık uygulama dependency set'ine dahil.

Improved

  • FormBuilder dokümanı. FormBuilder rehberi translatable builder'ları listeler ve özel rehbere bağlanır.
  • Çöp kutusuz File Manager dokümanı. File Manager rehberi, enableTrash=false durumunda tekil ve toplu silmelerin kalıcı silmeye yönlendiğini ve bulk delete için force_delete=true gönderildiğini açıklar.
  • Lvntr builder skill dokümanları. Proje ajan rehberi FormBuilder translatable alanlarını kapsar; gelecekte üretilen admin formlar desteklenen API'yi kullanır.

Upgrade

Güncelleme sonrası migration ve frontend build çalıştırın:

composer update lvntr/laravel-starter-kit
php artisan sk:update
php artisan migrate
npm install
npm run build

Kendi dil/settings akışını özelleştirmiş uygulamalar general.languages üzerinden okunan aktif dil listesini doğrulamalı. Mevcut düz string kolonlar otomatik taşınmaz; bir model attribute'unu Spatie HasTranslations altına almadan önce kolonları aşamalı migration ile JSON'a çevirin.

2026-05-02 — v13.4.9

Minor sürüm — Dosya Yöneticisi favoriler, çöp kutusu, geri yükleme, kalıcı silme, kopyalama ve yeniden adlandırma

Dosya Yöneticisi’nde v13.4.8’de placeholder olarak görünen yüzeyler artık gerçek özelliklere dönüştü. Favoriler ve Çöp Kutusu gerçek hızlı-erişim görünümleri; klasör/dosya tile’ları yıldızlanabiliyor; silinen öğeler varsayılan olarak çöp kutusuna taşınıyor; çöp kutusundaki öğeler geri yüklenebiliyor veya kalıcı olarak silinebiliyor; çöp görünümünde Çöpü Boşalt aksiyonu var. Dosyalar context menüden çoğaltılabiliyor ve yeniden adlandırılabiliyor. Bu sürüm iki migration (file_favorites ve media soft delete), yeni backend action/query/request sınıfları, yeni File Manager route’ları, genişletilmiş EN/TR dil key’leri ve günlük çalışan file-manager:purge-trash komutu getirir. Mevcut uygulamalar composer update lvntr/laravel-starter-kit && php artisan sk:update && php artisan migrate && npm install && npm run build çalıştırmalı.

Added

  • Favoriler. Yeni file_favorites tablosu ve FileFavorite modeli, klasör/dosyaları owner context’e göre yıldızlı tutar. FavoritesContentsQuery sidebar’daki Favoriler görünümünü besler; FolderContentsQuery artık öğeleri is_favorited ile işaretler; grid ve context menüler Add/Remove Favorite aksiyonlarını gösterir.
  • Çöp kutusu ve geri yükleme akışı. enableTrash açıkken dosya ve klasörler soft-delete ile çöp kutusuna taşınır. TrashContentsQuery Çöp Kutusu hızlı görünümünü besler; silinmiş tile’lar silinme zamanını gösterir; çöp context menüleri Restore / Permanently Delete aksiyonlarına döner.
  • Çöpü Boşalt. EmptyTrashAction ve DELETE /file-manager/trash/empty, mevcut context’teki tüm çöp öğelerini kalıcı olarak siler; dosyalar klasörlerden önce, klasörler ise çocuklar önce olacak şekilde post-order silinir.
  • Dosya kopyalama ve yeniden adlandırma. Dosyalar photo (copy).jpg / photo (copy 2).jpg gibi çakışmasız isimlerle çoğaltılabilir ve shipped dialog + PATCH /file-manager/files/{media} endpoint’iyle yeniden adlandırılabilir.
  • Trash purge komutu. php artisan file-manager:purge-trash --days=7, seçilen yaştan eski File Manager çöpünü kalıcı olarak siler. routes/console.php içinde günlük schedule edilmiştir.
  • enableTrash prop’u. FileManager varsayılan olarak soft-delete davranışıyla gelir; :enable-trash="false" verildiğinde çöp kutusu akışı kapatılıp doğrudan kalıcı silme davranışı kullanılabilir.

Security

  • Context doğrulaması merkezileştirildi. FileManagerContextRequest, sanal görünümler ve item mutasyonlarında geçerli File Manager context’ini tutarlı şekilde doğrulayıp çözer; favorites/trash endpoint’lerinin normal klasör içerik kontrollerinden sapma riski kapandı.
  • Soft-delete scope sertleştirildi. Geri yükleme, kalıcı silme, kopyalama, yeniden adlandırma ve favori action’ları öğeleri açıkça mevcut context’e scope eder ve gerektiği yerde withTrashed() / onlyTrashed() kullanır; cross-context erişim engellenir, trashed öğeler yalnız doğru yollarda bulunur.
  • Klasör geri yükleme cascade korumaları. Trashed bir klasör geri yüklenirken alt klasörleri ve File Manager media kayıtları transaction içinde geri yüklenir. Parent hâlâ çöpteyse işlem reddedilir; parent kalıcı silinmişse orphan oluşmaması için öğe root’a geri döner.

Fixed

  • Toplu force delete artık trashed öğeleri buluyor. BulkDeleteAction, force=true durumunda withTrashed() kullanır; Trash görünümünden kalıcı silme, zaten soft-delete edilmiş öğeleri artık kaçırmaz.
  • Dil key çakışması düzeltildi. labels.details artık detay bölümü array’i; action label labels.details_action oldu. Böylece dosya detay dialog’u label’ları context-menü aksiyon string’iyle ezilmiyor.
  • Collection scope sıkılaştırıldı. Trash purge ve kalıcı silme yalnız File Manager media kayıtlarını (collection_name = files) etkiler; avatar, logo, editor upload veya diğer MediaLibrary collection’larına dokunmaz.

Upgrade

Güncelleme sonrası migration çalıştırın:

composer update lvntr/laravel-starter-kit
php artisan sk:update
php artisan migrate
npm install
npm run build

API response tarafında breaking değişiklik yok. File Manager stub’larını özelleştirmiş uygulamalar sk:update --force kullanmadan önce özellikle FileManager.vue, useFileManager.ts, FileGrid.vue, FileManagerController.php, routes/web/file-manager-route.php, lang/{en,tr}/sk-file-manager.php, yeni request/action/query dosyaları ve iki migration ile kendi dosyalarını karşılaştırmalı.

2026-04-30 — v13.4.8

Minor sürüm — Dosya Yöneticisi UX yenilemesi (sidebar + stats + details + arama)

Dosya Yöneticisi UX yenilemesi — backend aynı, route'lar aynı, media tablosu aynı; yeni bir kabuk. Tek-kolon grid yerine sidebar + ana-kolon layout'u; üç yeni shipped component (FileManagerSidebar, FileDetailsDialog, FileManagerStats); mevcut klasörü client-side filtreleyen üst-bar arama kutusu; ve yeni girişlerle genişletilmiş sağ-tık menüsü (Yeni sekmede aç, Önizle, Paylaş, Kopyala, Yeniden Adlandır, Favorilere Ekle, Detaylar). Önceden belgelenmiş tüm davranışlar — yüklemeler, drag-and-drop taşıma, toplu silme, image lightbox, preview dialog'u, özel context'ler, settings, permission'lar — birebir aynı çalışır; değişiklik tamamen shipped frontend (FileManager.vue + üç yeni component + types.ts + lang/{en,tr}/sk-file-manager.php). Yeni composer veya npm bağımlılığı yok, migration yok, config yok, permission girdisi yok. Mevcut consumer uygulamaları composer update lvntr/laravel-starter-kit && php artisan sk:update && npm install && npm run build çalıştırarak yamayı çeker; breaking change yok.

Added

  • FileManagerSidebar.vue — dairesel storage-kullanım halkası, hızlı-erişim listesi, klasör ağacı ve "Yeni Klasör" butonuyla sol panel. Storage halkası, circumference - dashOffset doluluğuyla bir SVG çember kullanır ve renk-bandı eşiği uygular (primary < 70 %, amber 70–90 %, rose ≥ 90 %); kullanılan byte'lar fm.contents.stats.total_size'tan gelir, kota şimdilik backend setting'i bağlanana kadar görsel olarak makul 10 GB default'tur. Klasör ağacı, taşıma modalı'nın zaten yüklediği fm.tree verisini tekrar kullanır. Hızlı-erişim hedefleri: Tüm Dosyalar root'a name asc sıralı döner, Son Yüklenenler root'a date desc sıralı döner, Favoriler ve Çöp Kutusu yaklaşan özelliğin placeholder'ı olarak yeni coming_soon toast'unu gösterir.

  • FileDetailsDialog.vue — dosya detayları modali (Ad, Tip, Boyut, Yüklenme, Klasör ve resimlerde Boyutlar). Resim boyutları async yüklenir — dialog file.url'a karşı gizli bir new Image() tetikler ve onload çalıştığında naturalWidth × naturalHeight'ı render edilen satıra düşer. Dialog, sağ-tık menüsündeki downloadFile handler'ını yeniden kullanan bir "İndir" footer butonuyla gelir; böylece action yüzeyleri hizalı kalır. Dosya context menüsündeki yeni "Detaylar" girişinden açılır.

  • FileManagerStats.vue — üst-bar stats widget'ı (Toplam Dosya, Toplam Boyut, Klasör Sayısı, Favoriler, Son Yükleme). Yatay bir icon-tinted kart sırası render eder (light'ta bg-{renk}-100, dark'ta bg-{renk}-900/40). Klasör sayısı tüm nested ağacı dolaşır (flattenTree(fm.tree.value)); son yükleme mevcut klasördeki en yeni created_at'i yansıtır ve "Az önce / X dk / X sa / X g / locale-tarih" formatında yeni stats.time_* key'leri üzerinden gösterilir.

  • Üst-bar arama. Body'nin üzerinde bir IconField + InputText şeridi, fm.contents.folders ve fm.contents.files'ı name / file_name üzerinde case-insensitive includes ile filtreler — yeni filteredFolders / filteredFiles computed'leri üzerinden yüzeye çıkar. Filtre render edilen klasörle sınırlıdır; navigasyon fm.loadContents() bir sonraki çağrıda filtreyi örtük olarak temizler.

  • Genişletilmiş dosya context menüsü — Aç / Önizle / İndir / Paylaş / Taşı / Kopyala / Yeniden Adlandır / Favorilere Ekle / Detaylar / Sil. "Aç" artık dosyayı yeni sekmede açar (window.open(file.url, '_blank', 'noopener,noreferrer')); "Önizle" mevcut lightbox / dialog akışını korur; "Paylaş" mutlak dosya URL'sini panoya kopyalar (navigator.clipboard.writeText(...)), başarıda yerelleştirilmiş "Bağlantı kopyalandı" toast'u, izin reddinde coming_soon toast'u gösterir; "Detaylar" yeni dialog'u açar; "Kopyala", "Yeniden Adlandır", "Favorilere Ekle" yaklaşan özelliklerin placeholder'larıdır. Destructive Sil satırı, ayrı styling için yeni bir fm-menu-danger class'ı alır.

  • Klasör context menüsü — Sil'den önce "Favorilere Ekle" (placeholder) eklenir. Dosya menüsündeki placeholder'larla aynı coming_soon toast pattern'i.

  • types.ts — ViewMode = 'grid' | 'list' ve QuickView = 'all' | 'recent' | 'favorites' | 'trash' eklenir. ViewMode yaklaşan list-view renderer için ayrılmıştır (şu an yalnızca grid); QuickView sidebar hızlı-erişim akışı tarafından tüketilir. Mevcut export'lar değişmez.

  • lang/{en,tr}/sk-file-manager.php — yeni key'ler. Üst seviye: link_copied, coming_soon. Label'lar: upload_new, preview, share, copy, add_to_favorites, details, search_placeholder, view_grid, view_list, files_section, folders_section, no_results. Yeni iç içe gruplar: labels.sidebar.*, labels.stats.*, labels.details.*.

Removed

  • FileManager.vue'dan eski header back-button + sort dropdown kaldırıldı. Önceki kabukta header'da ← back butonu + sort key için Select dropdown + yön-toggle butonu vardı; navigasyon artık sidebar (klasör ağacı + breadcrumb) üzerinden, sıralama ise hızlı-erişim akışı ("Son Yüklenenler" = setSort('date', 'desc')) üzerinden gerçekleşir. useFileManager composable'ı doğrudan çağrı yapanlar için hâlâ setSort / toggleSortDirection export eder.

Upgrade

Breaking change yok. Mevcut consumer uygulamaları composer update lvntr/laravel-starter-kit && php artisan sk:update && npm install && npm run build çalıştırır — sk:update yeni shipped dosyaları ve genişletilmiş dil key'lerini çeker. Wire üzerindeki veri şekli değişmez; backend değişmez.

2026-04-26 — v13.4.7

Tek-fix patch — Tiptap'ın EditorInput ayağa kalkarken yazdığı Duplicate extension names found: ['link'] uyarısını susturur. Tiptap v3'ün @tiptap/starter-kit'i Link extension'ını default olarak bundle'lamaya başladı, ama editör hâlâ @tiptap/extension-link'i opsiyonel props.links branch'i üzerinden kendi openOnClick: false, autolink: true config'imizle push ediyordu — yani aynı editöre iki link registration'ı giriyordu. Düzeltme StarterKit çağrısında tek bir config flag (link: false); böylece bundle'lanmış kopya devre dışı, manuel-push branch'imiz tek kaynak. Davranış hem props.links === false (Link hiç yok) hem de props.links === true (sadece manuel-push) için birebir aynı; sadece console gürültüsü kalkıyor. Mevcut consumer uygulamaları composer update lvntr/laravel-starter-kit && php artisan sk:update çalıştırır — migration yok, config yok, breaking yok.

Fixed

  • EditorInput.vue — duplicate Link extension uyarısı susturuldu. Tiptap v3'ün @tiptap/starter-kit'i Link extension'ını default olarak içeriyor; editör ayrıca props.links opsiyonel branch'i üzerinden @tiptap/extension-link'i de manuel olarak push ediyordu — sonuç: editör console'da Duplicate extension names found: ['link'] uyarısıyla ayağa kalkıyordu. StarterKit.configure({ heading: { levels: [2, 3, 4] }, link: false }) ile bundle'lanmış kopya devre dışı bırakıldı; tek kaynak artık manuel-push branch'i (kendi openOnClick: false, autolink: true config'imizle). props.links === false durumunda Link tamamen kalkıyor; props.links === true durumunda sadece manuel-push branch'i çalışıyor — aynı davranış, uyarı yok.

Upgrade

Breaking change yok. composer update lvntr/laravel-starter-kit && php artisan sk:update patch'i çeker — düzeltme sk:update'in zaten takip ettiği aynı shipped Vue dosyası üzerinden geliyor; ek adım gerekmez.

2026-04-26 — v13.4.6

Patch sürüm — Vite optional-peer-dep stub'ı + sk:update package.json merge

EditorInput öncesi bir kit sürümünden (13.4.0 ve öncesi herhangi bir kurulum) 13.4.2+ sürümlerine yükseltirken yüzeye çıkan iki ilişkili build/upgrade düzeltmesi. Paketin package.json'u artık @tiptap/* setini peerDependencies + peerDependenciesMeta.optional ile bildirmiyor — bu bildirimler, deps consumer'ın project root'unda yüklü olsa bile, vendor/lvntr/laravel-starter-kit/ altından resolve edilen import'larda Vite'ın optional-peer-dep stub fallback'ini (__vite-optional-peer-dep:@tiptap/extension-table:@lvntr/starter-kit:false) tetikliyordu. Sonuç: build sırasında "Table" is not exported by …, runtime'da does not provide an export named 'BubbleMenu' — ikisi de Vite'ın stub modülünden (export default {}; throw …) geliyordu, gerçek paketten değil. Ayrıca sk:update artık sk:install'in mergePackageJson() adımını mirror'lıyor; böylece yeni @tiptap/* seti yükseltmede consumer'ın package.json'una otomatik düşüyor — daha önce yalnızca fresh install bunu çekiyordu, <13.4.2'den yükselen her consumer 16 dependency entry'sini elle kopyalamak zorundaydı. Ortak key'ler için stub-version-wins, user extra'ları korunur, tekrar çalıştırılınca idempotent.

Fixed

  • Paket package.json — @tiptap/* seti için peerDependencies + peerDependenciesMeta kaldırıldı. Paket composer üzerinden dağıtılıyor (npm'de yayınlanmadı) — peer-dep bildirimlerinin npm install üzerinde hiçbir etkisi yoktu; pratikte tek etkileri Vite'ın tryNodeResolve fallback'iydi. Bare bir import (import { Table } from '@tiptap/extension-table'), normal node_modules walk-up'tan resolve edilemediğinde — paket vendor/ altında olunca tetiklemesi kolay — Vite importer'in en yakın package.json'una bakıyor, dep'i optional peer olarak buluyor ve hata yerine __vite-optional-peer-dep:<dep>:<parent>:<isRequire> döndürüyordu. Stub export default {}; throw new Error("Could not resolve …") olarak yüklenir — named export yok; build'deki yanıltıcı "Table" is not exported by … ve @tiptap/vue-3/menus subpath'i için runtime'daki does not provide an export named 'BubbleMenu' bu yüzden çıkıyordu. Bildirimler kaldırılınca düz node_modules resolve geri devreye giriyor, project root'a kadar walk-up edip gerçek paketleri buluyor.

  • sk:update artık stubs/package.json'u consumer'ın package.json'una merge'liyor. UpdateCommand daha önce sadece app/, config/, resources/ ve routes/ altındaki dosyalara dokunuyordu — projenin package.json'una asla. Bu yüzden 13.4.2'nin stub'a eklediği 16 @tiptap/* entry'si, composer update lvntr/laravel-starter-kit && php artisan sk:update yapan consumer'lara hiç ulaşmıyordu. Yeni adım (handle()'da 4c), InstallCommand::mergePackageJson()'u mirror'lıyor: stub key'leri root'ta kazanır, array_merge-d dependencies/devDependencies (sıralı), user extra'ları korunur, sadece render'lanan JSON gerçekten farkını yansıttığında yazar (tekrar çalıştırma no-op). Özet, değişikliği package.json (merged stub dependencies — run npm install) olarak gösterir; böylece kullanıcı sonradan npm install çalıştırması gerektiğini bilir.

Upgrade

Breaking change yok. Mevcut consumer uygulamaları composer update lvntr/laravel-starter-kit && php artisan sk:update && npm install && npm run build çalıştırır — sk:update artık eksik @tiptap/* entry'lerini package.json'unuza sync'ler ve Vite stub'lar yerine gerçek paketleri resolve eder.

2026-04-26 — v13.4.5

Patch sürüm — code-review taraması (API hiyerarşi + role-data + 2FA loading + permission directive + i18n)

v13.4.x yüzeyinin takip eden bir kod incelemesinden çıkan küçük bir bulgu paketini kapatır. İki güvenlik / bilgi-sızdırma düzeltmesi (API kullanıcı listesi artık admin panelinin uyguladığı aynı role-hiyerarşi filtresini uyguluyor; rol JSON data endpoint'i artık edit/destroy action'larıyla aynı CanManageRoleQuery guard'ından geçiyor), bir UX düzeltmesi (2FA enable/disable butonları yalnızca happy path'te değil, hata yollarında da loading state'lerini sıfırlıyor), bir latent-bug düzeltmesi (v-role directive'i Inertia paylaşımlı prop'unun yanlış key'ini okuyup sessizce hep false döndürüyordu) ve bir i18n temizliği (useApi composable'ının hata toast'ları ve sentezlenmiş envelope mesajları artık hardcoded Türkçe stringler yerine sk-message.* key'leri üzerinden geçiyor). Tüm değişiklikler wire üzerinde additive — aynı response shape, aynı status kodları, aynı UI. Üç regression test'i iki güvenlik düzeltmesini koruyor. Mevcut tüketici uygulamalar php artisan sk:update ile yamaları çeker; migration yok, config yok, breaking yok.

Security

  • Api/UserController::index artık UserDatatableQuery'ye delegasyon yapıyor — admin paneliyle aynı role-hiyerarşi filtresi. Önceki haliyle API, UserDatatableQuery'nin uyguladığı whereDoesntHave('roles', sort_order < me) clause'unu atlayan kendine özel bir DatatableQueryBuilder zinciri kullanıyordu. Sonuç: users.read izni olan ama system_admin olmayan bir API tüketicisi GET /api/v1/users ile her üst-rank kullanıcıyı — system_admin hesapları dahil — görebilirdi; admin UI ise onları gizliyordu. Controller artık UserDatatableQuery'yi method-inject edip doğrudan response($request->user()) döndürüyor. Query'nin allowlist'leri legitimate API çağrılarının wire kontratı değişmesin diye first_name, last_name, email, status, id, created_at sortable key'leri (önceden API'a özeldi) ile genişletildi. Yeni tests/Feature/Api/UserTest.php "hides higher-rank users from non-system_admin api callers" regression test'iyle korunuyor.

  • Admin/RoleController::data artık rol JSON'unu döndürmeden önce CanManageRoleQuery çalıştırıyor. data(), edit()'in JSON kardeşidir (admin rol formu bunu useApi().get('/admin/roles/{role}/data') ile pre-fetch ediyor). edit() ve destroy() zaten role hiyerarşisini zorlamak için CanManageRoleQuery::check()'ten geçiyordu; data() geçmiyordu — bu yüzden alt-rank bir admin, render edeceği form hiyerarşi-aware olduğu halde, üst-rank bir rolün tüm permission setini JSON üzerinden okuyabiliyordu. Kontrol artık data()'nın en üstüne inline edildi (abort(403) mismatch'te), edit() davranışını mirrorluyor. İki yeni tests/Feature/Admin/RoleManagementTest.php regression test'iyle korunuyor ("forbids non-system_admin from reading higher-rank role data" + same/lower rank için pozitif kardeş).

Fixed

  • 2FA enable/disable butonları artık hata durumunda takılı kalmıyor. Profile/components/TwoFactorTab.vue, Fortify'a çağrı atmadan önce twoFactorProcessing = true set ediyordu ama yalnızca success branch'inde sıfırlıyordu. Bir axios 4xx/5xx (tipik: süresi dolmuş bir oturum, password-confirm timeout'u) ya da bir Inertia router.reload hatası, butonu tam sayfa yenilemeye kadar spinner'da bırakıyordu. enableTwoFactor() ve disableTwoFactor() artık flag'i bir finally bloğunda sıfırlıyor; herhangi bir hata buton'u tekrar tıklanabilir + bir toast olarak yüzeye çıkıyor (donmuş UI yerine).

  • v-role directive'i artık doğru Inertia paylaşımlı prop key'ini okuyor. resources/js/plugins/permission.ts auth.roles'u kontrol ediyordu ama HandleInertiaRequests kullanıcı rol isimlerini auth.role_names altında paylaşıyor. Directive sessizce hep false'a düşüyordu — <div v-role="'system_admin'"> markup'ı, actor'ün rolü ne olursa olsun hiç görünmüyordu. Plugin artık auth.role_names'i okuyor. Plugin dosyasındaki duplicate useCan export'u (aynı yanlış key'i okuyordu) da kaldırıldı — kanonik useCan() @/composables/useCan altında yaşıyor ve zaten doğruydu, yani uygulama kodu etkilenmedi. Plugin dosyası artık yalnızca PermissionPlugin'i export ediyor (v-can + v-role kayıt eder).

  • useApi composable'ı hata mesajları sk-message.* i18n key'leri üzerinden akıyor. resources/js/composables/useApi.ts üç hardcoded Türkçe hata stringi içeriyordu (non-JSON response için sentezlenmiş envelope, network-failure toast detayı, toast summary). trans('sk-message.invalid_response'), trans('sk-message.request_failed', { status }), trans('sk-message.network_error'), trans('sk-message.error_summary') ile değiştirildi. Dört yeni key hem lang/en/sk-message.php hem lang/tr/sk-message.php içine eklendi. EN-locale kullanıcıları artık bir API çağrısı normal envelope yolu dışında başarısız olduğunda Türkçe metin görmüyor.

New

  • İki güvenlik düzeltmesi için regression test'leri. tests/Feature/Api/UserTest.php hides higher-rank users from non-system_admin api callers test'ini kazanıyor — RoleEnum index'i üzerinden role hiyerarşisini seed ediyor, users.read + admin rolünü api guard'ına da mirrorlayıp (Spatie'nin Guard::getDefaultName()'i Passport::actingAs altında api'ye geçiyor), bir admin kullanıcısına web + api versiyonlarını birlikte assign ediyor ve response'un üst-rank system_admin peer + acting system_admin user'ını dışlayıp same-rank admin peer'ı içerdiğini assert ediyor. tests/Feature/Admin/RoleManagementTest.php ikisini kazanıyor: forbids non-system_admin from reading higher-rank role data (admin /admin/roles/{system_admin}/data'da 403 alır) ve allows non-system_admin to read lower-rank role data (admin /admin/roles/{user}/data'da 200 alır).

2026-04-25 — v13.4.4

Patch sürüm — system-admin log görüntüleyici (/logs)

storage/logs/ altındaki Laravel log dosyalarını listelemek, aramak ve silmek için yalnızca bakım rolüne açık bir admin bölümü ekler. Kendi içinde tamamlanmıştır — yeni composer/npm bağımlılığı, migration veya permission girdisi gerekmez. Yalnızca system_admin kullanıcılarına görünür; geri kalan herkes paneli aynı şekilde görür. Tamamı additive.

Added

  • /logs admin bölümü — yalnızca system-admin log görüntüleyici. "Sistem" başlığı altındaki yeni sidebar öğesi storage/logs/ içeriğini bir SkDatatable'da listeler (dosya adı, kanal tipi, boyut, değiştirilme zamanı, aktif flag'i); dosya bazlı görüntüleyici sayfası ise yapısal filtreler (seviye, tarih aralığı, anahtar kelime) altında cursor sayfalanmış kayıt akışını gösterir. Tekli + toplu silme aynı endpoint üzerinden kısmi-başarı semantiğiyle çalışır — aktif dosyalar (bugünün günlük log'u, son 5 saniyedir yazılanlar) tek tek reddedilir ve failed[] listesinde geri döner; geri kalan dosyalar silinir. Her silme batch'i LogFilesDeleted event'i dispatch eder; yeni LogActivityForLogFilesDeleted listener'ı log_name = system altında bir spatie/activitylog kaydı yazar — silme işlemi Admin → Activity Logs sayfasında otomatik görünür.

  • app/Domain/Logs/ bounded context. Dört DTO (LogFileDTO, LogEntryDTO, LogEntryFilterDTO, DeleteLogFilesDTO), iki query (LogFileQuery dosya listesi için, LogEntryQuery kayıt stream'i için), bir action (DeleteLogFilesAction), bir event/listener çifti ve stateless LaravelLogParser servisi. LogEntryQuery::paginate() dosyayı fopen('rb') + 64KB ile sınırlı fgets() ve byte offset cursor ile okur; bellek kullanımı dosya boyutundan bağımsız olarak sabit kalır; çok satırlı stack trace'ler ait oldukları kayda eklenir; ilk Laravel-format başlığından önce gelen (veya hiç başlık içermeyen dosyalarda kalan) satırlar tek bir raw LogEntryDTO olarak basılır (is_raw = true, gri chip, gizli timestamp) — yani dosya içeriği sessizce kaybolmaz. Yapısal filtre uygulandığı an (level / from / to / keyword) raw entry'ler doğal olarak listeden düşer.

  • logs.* isimli route grubu. routes/web/log-route.php beş route içerir — index, dtApi, show, entries, destroy — hepsi role:system_admin ile sarılır. {filename} parametre kısıtı ([A-Za-z0-9._-]+\.log) hem show hem entries üzerinde zorlanır; path traversal ve .log olmayan istekler controller'a hiç ulaşmaz. Bölüm role-gated olduğu (permission-gated olmadığı) için dosya routes/web.php içindeki $routesWithoutPermissionMiddleware allowlist'ine eklenmiştir.

  • lang/{en,tr}/sk-log.php çeviri dosyası. Tüm UI metinleri (filtre etiketleri, boş durumlar, silme onayları, hata sebep kodları) iki dilde de sk-log.* namespace'i altındadır. Yeni sk-menu.logs key'i sidebar'daki menü öğesini etiketler.

Security

  • Üç katmanda path-traversal koruması. Güvenli dosya adı regex'i ^[A-Za-z0-9._-]+\.log$ (1) route parametre kısıtında, (2) DeleteLogFilesRequest kurallarında ve (3) DeleteLogFilesAction::execute() içinde (defence in depth) zorlanır. Kalan her şey log.invalid_filename olarak failure döner ya da route binding'den 404 alır — disk path'i ham input'tan inşa edilmez.

  • Aktif dosya silme reddi. LogFileQuery::isActive(), bugünün günlük dosyasını (laravel-{today}.log) ve mtime'ı son 5 saniye içinde olan her dosyayı işaretler. DeleteLogFilesAction işaretli dosyaları item-bazlı reason: 'active_file_protected' ile reddeder; toplu submit, Laravel'in o anda yazdığı dosyayı kazara truncate edemez.

  • role:system_admin route gate'i, permission girdisi yok. Görüntüleyici bilinçli olarak config/permission-resources.php'ye eklenmemiştir. admin rolü vermek bunu açmaz; yalnızca özel system_admin rolü açar. system-admin olmayan kullanıcılar route'a 403 alır ve menü öğesini hiç görmez — özellik onlara görünmez.

  • 64KB satır okuma sınırı. LogEntryQuery fgets($handle, 65536) çağırır; sınırsız uzunlukta tek satırlık bir kayıt process belleğini tüketemez. Uzun satırlar isteği abort etmeden temiz şekilde truncate olur.

2026-04-25 — v13.4.3

Patch sürüm — zengin dikey tab'lar + datatable per_page üst sınırı

TB builder üzerinden daha zengin bir dikey tab görünümü (icon tile, description satırı, trailing badge veya check) ve DatatableQueryBuilder tarafında ?per_page= parametresi için opsiyonel üst sınır geliyor. Değişikliklerin tamamı additive — breaking yok. sk:update yeni TabBuilder Vue bileşenlerini, yeniden yazılmış _tabs.scss'i ve EN/TR sk-setting.tab_descriptions dil anahtarlarını taşır; paket katmanındaki max_per_page config için composer update yeterli.

Added

  • TB.item() zengin dikey tab fluent metodları. Dört yeni fluent metod: .description(text) label altında ikincil bir satır, .iconColor(color) renkli icon tile preset'i (13 renk: blue, amber, emerald, purple, teal, red, indigo, slate, pink, orange, cyan, green, yellow), .badge(value, severity?) sağ tarafta badge (5 severity: success, warn, info, danger, secondary) ve .checked() sağ tarafta yeşil check (badge üzerinde önceliklidir). Mevcut tab tanımları olduğu gibi çalışır. Ayarlar → Genel sayfası yeni API'yi kullanacak şekilde güncellendi (per-tab description + icon color); kanonik örnek olarak hizmet veriyor. Yeni i18n bloğu sk-setting.tab_descriptions yedi ayar tabını kapsar.

  • STARTER_KIT_DATATABLE_MAX_PER_PAGE env var + config('starter-kit.datatable.max_per_page'). DatatableQueryBuilder üzerinde ?per_page= parametresi için opsiyonel üst sınır. Anahtar tanımlı değilse 100'e düşer.

Security

  • DatatableQueryBuilder — ?per_page= üst sınırı zorlanır. Önceki sürümlerde bir istemci ?per_page=99999 gönderip builder'ı tüm tabloyu tek payload olarak materialise etmeye zorlayabiliyordu. Yeni tavan (config('starter-kit.datatable.max_per_page'), default 100) değeri sessizce kırpıyor — tavanın altında her şey aynı çalıştığı için meşru çağrılar etkilenmiyor.

Improved

  • Dikey tab sidebar — .isCard(true) ile PrimeVue Card sarmalayıcı. Tab'lar seviyesinde (per-tab değil) ayarlanır; dikey sidebar daha az iç padding'le bir Card içine sarılır. Yeni icon tile + description alanlarıyla birleştirildiğinde Ayarlar sayfasının sidebar'ı kutudan çıkar çıkmaz modern admin-panel düzenine uyuyor.

Fixed

  • Branding — legacy "Starter Kit 12" referansları. İki yer hâlâ "Starter Kit 12" diyordu — config/scramble.php API açıklaması ve app.blade.php fallback title; her ikisi de artık "Starter Kit 13" diyor.

2026-04-24 — v13.4.2

Patch sürüm — Tiptap editor input, şifre üreticisi, dashboard hoş geldin mesajı + güvenlik sertleştirmesi

Zengin metin editörü olarak çalışan FB.editor() FormBuilder alanı (arkasında Tiptap v3) server-side HtmlSanitizer utility'si ile birlikte, FB.password()'a crypto-safe şifre üreticisi ve Ayarlar → Genel altında editor ile yazılan admin dashboard hoş geldin mesajı bu sürümde geliyor. Dosya yükleme, editor-scoped upload'ların gruplu kalması için opsiyonel folder_name parametresi kazandı; FileManager artık HTTP 413 Payload Too Large için özel bir hata mesajı gösteriyor. Değişikliklerin tamamı additive — breaking yok. sk:update publish edilmiş dosyaları (yeni Vue bileşenleri, HtmlSanitizer, dil anahtarları) taşır; paket-katmanı değişiklikleri için composer update yeterli.

Added

  • Tiptap tabanlı FB.editor() FormBuilder input'u. Tiptap v3 üzerine kurulan yeni bir form alanı tipi; bubble menu, link / image / table / task list / text align / text color / text style ve placeholder extension'ları içerir. Araç çubuğu düzeni .toolbar('minimal' | 'standard' | 'full') ile seçilir; resim yüklemeleri FileManager context'i üzerinden opsiyonel folder-grouping parametresiyle yönlendirilir; yardımcı bileşenler (EditorColorPalette, EditorImagePicker) renk ve görsel seçici akışlarını karşılar. Çeviriler lang/{en,tr}/sk-editor.php dosyalarında. İçerik kaydedilirken yeni App\Support\HtmlSanitizer üzerinden geçer — yalnızca allowlist'teki tag / attribute / URL scheme'leri DB'ye yazılır.

  • FB.password().generator() — crypto-safe şifre üreticisi. Parola alanlarının yanına generate butonu ekleyen opt-in fluent metodu; crypto.getRandomValues() kullanır. Default'lar bilinçli olarak Password::defaults()'tan daha sıkı (16 karakter, mixed case + harf + rakam + sembol) — böylece üretilen her değer ilk submit'te proje-wide parola politikasını geçer. Aynı değişiklikte yeniden yazılmış custom eye toggle ile password ve password_confirmation alanları InputGroup içinde birebir aynı görünüyor. PrimeVue <Password> artık yalnızca .feedback() ile strength meter'a opt-in edildiğinde kullanılır — diğer tüm kullanımlar daha hafif InputText + eye yoluna düşer. Admin User formunda kutudan çıkar çıkmaz aktif.

  • Admin dashboard hoş geldin mesajı. Ayarlar → Genel altına FB.editor() ile yazılan opsiyonel bir welcome_message WYSIWYG alanı geldi. Dashboard sanitize edilmiş HTML'i Inertia prop'u olarak paylaşıyor, resources/js/pages/Admin/Dashboard/Index.vue ise sk-prose container'ında v-html ile render ediyor. Değer hem yazılırken (FormRequest prepareForValidation hook'u) hem okunurken (DashboardController defense-in-depth geçişi) sanitize ediliyor; böylece on-disk değer bozulsa bile eski kayıtlardaki kötü niyetli HTML frontend'e ulaşmıyor.

  • folder_name upload parametresi. POST /file-manager/files artık opsiyonel folder_name string'i kabul ediyor (nullable, max:100, sıkı regex: yalnızca harf / rakam / boşluk / tire / altçizgi — path traversal ve keyfi karakter riski validation'da kapatıldı). Geçildiğinde UploadFileAction::ensureManagedFolder mevcut context için o isimde root-level bir klasörün varlığını atomik şekilde garanti ediyor ve upload'ı içine koyuyor. Welcome-message editor'ü bu parametreyi kullanarak tüm inline görsel upload'larını tek "Welcome Message" klasörü altında gruplar — eski read-query side-effect pattern'i geri gelmiyor. Frontend'teki EditorImageUploadConfig aynı alanı folderName üzerinden expose ediyor.

Security

  • App\Support\HtmlSanitizer — tag, attribute ve URL scheme allowlist'i. Editor payload'larından allowlist'te olmayan tüm tag / attribute / URL scheme'lerini süzen yeni utility. URL işleme blocklist'ten allowlist'e çevrildi: relative URL'ler + http://, https://, mailto:, tel: kabul ediliyor — diğer her şey (blob:, data:, file:, ftp:, javascript:, vbscript:) reddediliyor. Kendi tests/Unit/HtmlSanitizerTest.php regression suite'i ile kapsanıyor.

  • SettingService::normalizeValue() — tüm yazma yollarında HTML sanitize. setValue() ve setGroup() her değeri paylaşılan normalizeValue() hook'undan geçiriyor. Yeni HTML_SAFE_KEYS whitelist'inde listelenen anahtarlar (şu an general.welcome_message) DB'ye ulaşmadan önce HtmlSanitizer::sanitize()'den geçiyor; yani FormRequest dışı tüm yazma yolları (tinker, scheduled command, queue job) da sanitize'e tabi — normal setting API'si üzerinden sanitize edilmemiş HTML DB'ye asla yazılamıyor.

  • Dashboard welcome message — defense-in-depth okuma sanitize'i. DashboardController::index saklanmış welcome message'ı Inertia'ya paylaşmadan önce HtmlSanitizer::sanitize()'den ikinci kez geçiriyor. Write-path sanitize'i gelmeden önce yazılmış tarihi kayıtlar ve drift etmiş veya manuel poison edilmiş DB değerleri browser'a ulaşamıyor.

  • UploadFileAction::ensureManagedFolder — concurrency-safe managed folder oluşturma. Ensure path'i DB::transaction içinde aday satıra lockForUpdate ile kilit koyuyor, unique-constraint yarışı için QueryException catch ile refetch'e düşüyor ve soft-deleted klasörleri yeniden oluşturmak yerine withTrashed() ile restore ediyor. Üç katman birlikte, iki paralel editor upload'unun aynı klasör adında deadlock'a girmesi veya soft-deleted bir satırı silip unique index'e çarpan bir sibling oluşturması yarışını kapatıyor.

  • UploadFileRequest — folder_name input'u sıkı validasyonla geçiyor. Yeni alan nullable|string|max:100|regex:/^[\pL\pN _-]+$/u kullanıyor; path-traversal ve keyfi karakterli içerik downstream'e değil, FormRequest sınırında reddediliyor.

Improved

  • FileManager upload hata mesajları. Client composable artık HTTP 413 (Payload Too Large) durumunda jenerik hata yerine yeni too_large çevirisini gösteriyor (EN + TR); diğer tüm non-200 yanıtlar ise client-side mesajda status code'u da taşıyor — upload hatalarını devtools network sekmesini açmadan teşhis etmek kolaylaştı.

  • Password alanı default render yolu. Yukarıdaki .generator() eklemesine ek olarak, default FB.password() render'ı PrimeVue <Password> yerine InputText + custom eye toggle'a geçti. <Password>'un kendi eye ikonunun InputGroup addon'larında kaybolma sorununu çözüyor ve password / password_confirmation alanlarının birebir aynı görünmesini sağlıyor. .feedback() çağrıldığında (strength meter yolu) hâlâ PrimeVue <Password> kullanılıyor. Yeni i18n anahtarları: generate_password, password_generated, password_generated_detail, show_password, hide_password (EN + TR).

Fixed

  • SettingsDefaultsQuery read path'i artık yazma yapmıyor. Önceki sürümde Ayarlar → Genel ekranı okunduğunda resolveWelcomeMessageFolderId() yan etkisi olarak FileFolder::firstOrCreate(...) çağırıyordu. Aynı isimde soft-deleted bir klasör barındıran install'larda unique index insert'i reddediyor ve admin, saf bir okuma ekranında 500 alıyordu. Folder ensure yolu artık yalnızca upload anında çalışan UploadFileAction::ensureManagedFolder'da; SettingsDefaultsQuery yeniden tamamen side-effect-free. Frontend'teki welcome_message_folder_id Inertia prop bağımlılığı da kaldırıldı — editor doğrudan folderName üzerinden çalışıyor.

  • Editor upload — stale blob: URL'lerinin form payload'una sızması engellendi. EditorInput.vue artık setContent({ emitUpdate: false }) sonrasında parent v-model'i elle senkronlıyor; taze bitmiş bir upload'tan geride kalan / kırık <img src="blob:..."> parçaları submit edilen HTML'de sunucuya gitmiyor.

2026-04-22 — v13.4.1

Patch sürüm — API response sertleştirme + Postman/Apidog sync + OAuth UUID fix

Bu sürüm, baştan sona elden geçirilen API response zarfı (trace-id pipeline, merkezi exception handler, leak kapatan controller patch'leri) ile iki yeni API client entegrasyonu (Postman ve Apidog sync) ve iki adet kurulum fix'ini (OAuth UUID uyumluluğu, otomatik Passport personal access client) birlikte getiriyor. Çoğu değişiklik additive (yeni body alanı + header'lar, yeni admin butonları) ama üç adet API-response davranışsal breaking noktası strict client'lar için önemli — detay için docs/UPGRADE.tr.md. Taze kurulumlar her şeyi otomatik alır; mevcut projeler upgrade rehberini takip etmelidir. sk:update publish edilmiş dosyaları taşır; controller patch'leri ve sonrası Passport adımı manueldir.

Security

  • Controller $e->getMessage() leak'leri kapandı (11 yer). FileManagerController (bulkDelete/createFolder/renameFolder/moveItem/deleteFolder/upload/deleteFile), Api/UserController::destroy ve Api/Auth/AuthController::login+twoFactorChallenge içinde to_api(null, $e->getMessage(), 4xx) pattern'i throw ApiException::* ile değiştirildi. Mesaj metni aynı şekilde client'a gidiyor ama artık merkezi handler'dan geçiyor — trace_id eşleniyor, 500+ log'lanıyor, X-Correlation-ID echo ediliyor. Iç LogicException mesajı yerine kontrollü ApiException tipine geçiş, gelecek refactor'larda iç mesaj sızıntısı riskini kapatıyor.

  • abort($code, 'msg') raw mesajı artık client'a sızmıyor. HttpExceptionInterface dalı $e->getMessage() yerine sabit defaultMessageForStatus() tablosunu kullanıyor. abort(400, 'SQL error: ...') çağrısı artık body'de "Bad request." döndürür; iç detay sadece APP_DEBUG=true iken debug.message alanında görülür. Controlled mesaj için throw ApiException::badRequest('...') kullanın.

  • Api/AuthController ham User model'i yerine UserResource dönüyor. register, login (default kind), twoFactorChallenge ve me endpoint'leri data.user için artık UserResource::toArray() çıktısı veriyor. Ham Eloquent serializasyonu $hidden'a güveniyordu; ileride eklenecek hassas bir alan unutulursa sessizce sızabilirdi. Resource artık kontrat — hangi alan client'a gidiyor açıkça yazılı.

Added

  • Postman sync — admin butonu + CLI. API Rotaları sayfasındaki "Postman'e Gönder" aksiyonu (ve php artisan postman:sync komutu) Scramble OpenAPI spec'ini Postman'in /import/openapi endpoint'ine folderStrategy=Tags parametresiyle push ediyor — tag'ler doğrudan folder'a dönüşüyor. Her sync önce taze koleksiyonu import ediyor, yeni UID'yi ayarlar tablosuna yazıyor, ardından eski koleksiyonu best-effort siliyor. import-first, delete-after sırası sayesinde Postman tarafında geçici bir hata veya geçersiz token, mevcut çalışan koleksiyonu kaybetmeden geçiyor. Yapılandırma: Settings → API Clients → Postman card'ı (API Key + Workspace ID; collection ID otomatik yönetiliyor).

  • Apidog sync — admin butonu + CLI. Aynı pipeline Apidog'un POST /v1/projects/{projectId}/import-openapi endpoint'ine inline JSON input ve OVERWRITE_EXISTING davranışıyla push ediyor. php artisan apidog:sync olarak da çağrılabiliyor. Yapılandırma: Settings → API Clients → Apidog card'ı (Access Token + Project ID).

  • Settings → API Clients tabı. Postman ve Apidog yapılandırması için ayrı card'lar içeren tek bir tab. Gizli alanlar (postman.api_key, apidog.access_token) config/settings.php'deki sensitive_keys listesi aracılığıyla DB'de encrypted tutuluyor. Eski POSTMAN_* .env anahtarları artık kullanılmıyor — mevcut değerler ayarlar tablosuna migrate ediliyor.

  • Ortak OpenApiExporter helper. İki sync Action'ı aynı exporter'ı paylaşıyor: scramble:export çalıştırıyor, storage/app/postman/ altına her çağrı için benzersiz bir geçici dosya yazıyor, finally bloğunda temizliyor — CLI komutu ve admin butonu eş zamanlı çalıştığında paylaşılan bir dosyada yarışmıyorlar. Spec değişmeden gönderiliyor: content-type rewrite yok, push edilen koleksiyon gerçek sunucu kontratını aynen yansıtıyor (client'lar kendi UI'larında body görünümünü istedikleri gibi raw/form-data arasında değiştirebilir).

Improved

  • Başarılı ve hatalı response'lar aynı trace_id altında eşleşiyor. Yeni AssignTraceId middleware'i API grubuna prepend edildi; her request'te UUID üretiyor ve $request->attributes->get('trace_id') üzerinden hem success (ApiResponse::toResponse) hem error (ApiExceptionHandler) dalı aynı id'yi pick-up ediyor. Body'de trace_id + header'da X-Request-ID + client'ın sanitize edilmiş X-Request-ID'si X-Correlation-ID olarak echo. Müşteri destek senaryolarında client log'u ile sunucu log'u tek id ile eşleşebiliyor.

  • ModelNotFoundException mesajı model ismini içeriyor. "The requested resource was not found." → "User not found." (veya Role, Product, …). ApiExceptionHandler::modelNotFoundMessage class_basename($e->getModel()) ile resolve ediyor. Bir önceki AGENTS.md vaadini karşılıyor; model sınıf adı zaten URL'den tahmin edilebildiği için güvenlik etkisi yok.

  • 429 Too Many Requests response'una Retry-After header'ı propagate ediliyor. ThrottleRequestsException::getHeaders() içindeki tüm rate-limit header'ları (Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) response'a kopyalanıyor. Rate-limit'e takılan client'lar artık kaç saniye bekleyeceğini mesajdan parse etmek yerine standart header'dan okuyabiliyor.

  • simplePaginate() desteği. to_api(Model::simplePaginate(15)) artık type error vermiyor; meta.has_more ile yetinen lightweight pagination için destek eklendi. LengthAwarePaginator ve CursorPaginator davranışı değişmedi.

  • to_api(paginator, 'msg', 201) artık pagination meta'sını kaybetmiyor. Helper'da paginator-detect 201/202 branch'lerinden önce çalışıyor; batch-create tipinde endpoint'ler bile meta üretiyor (önceki sürüm paginator'u ham nesne olarak serialize ediyordu — sessiz bug düzeldi).

  • ApiResponse DRY + final. paginated() ve paginatedCollection() meta üretim mantığı tek bir private helper'a çekildi. Sınıf artık final — subclass invariant'ı kırma riski kapandı. Controller'ların dönüş tipi imzasında davranışsal değişim yok; public API surface aynı.

  • Scramble ApiResponseExtension şema açıklamaları zenginleşti. Envelope'daki her alan için tanım + örnek + validation rule description eklendi. Multi-status şema (201 / 204 / 4xx / 5xx için ayrı Response) Scramble TypeToSchemaExtension API'siyle direct desteklenmediği için sonraki sürüme taşındı — OperationExtension ile modellenecek.

Fixed

  • OAuth migration'ları UUID uyumlu. oauth_access_tokens.user_id ve oauth_auth_codes.user_id artık foreignUuid (önceden foreignId / bigint unsigned); oauth_clients.owner_* artık nullableUuidMorphs. Starter kit'in UUID users.id primary key'i ile birlikte önceki uyumsuzluk login akışında SQLSTATE 1265: Data truncated for column 'user_id' hatasını tetikliyordu — login akışı artık kutudan çıkar çıkmaz temiz çalışıyor.

  • site:install Passport personal access client'ı otomatik oluşturuyor. passport:keys ile admin-user seed adımları arasına passport:client --personal --provider=users eklendi. Taze kurulumlar artık API token üretebiliyor; önceden operatörün manuel olarak bu komutu çalıştırması gerekiyordu.

  • 202 Accepted'ın ölü kodu temizlendi. to_api($data, '', 202) için 'Operation queued.' fallback'i zaten hiç devreye girmiyordu ($message default'u truthy). Helper tek bir mantıksal akışa sadeleşti.

  • ApiResponse::toResponse() request attribute'u değerlendiriyor. Önceki sürüm Responsable::toResponse($request) imzasını alıyordu ama $request'i kullanmıyordu — yeni middleware ile entegrasyon bu parametreye bağlı, artık değerlendiriliyor.

  • Exception handler match sıra kritikalitesi yoruma bağlandı. ApiException extends HttpException, HttpExceptionInterface dalından önce kalmalı — aksi halde custom API exception'lar generic abort() handling'e düşerdi. Fragile sıralama yorum satırıyla ve regression test (tests/Feature/Api/ApiResponseTest.php) ile güvence altına alındı.

New

  • Regresyon test dosyası: tests/Feature/Api/ApiResponseTest.php (16 test, 57 assertion). Envelope şekli, exception → status mapping, trace id eşleşmesi, 204 boş body, Retry-After propagation, debug sadece APP_DEBUG=true iken, sanitize edilmiş X-Correlation-ID echo — tüm kontrat testleri. Paketten örnek olarak vendor/lvntr/laravel-starter-kit/tests/examples/ApiResponseTest.php'tan kopyalanabilir.

  • sk:update otomasyon kapsamı genişledi. app/Http/Middleware/AssignTraceId.php ve app/Helpers/sk-helpers.php artık safe-update listesinde; php artisan sk:update bu iki dosyayı otomatik senkronize ediyor. ApiResponse.php ve ApiExceptionHandler.php zaten listedeydi.

Breaking

Detaylı migration adımları için docs/UPGRADE.tr.md. Özet:

  • abort($code, 'custom message') artık mesajı göstermiyor — ApiException::* throw kullanın.
  • ModelNotFoundException mesajı model adını içerir ("User not found."). Frontend regex eşleşmeleri güncellenebilir.
  • Api/Auth/AuthController data.user alanları UserResource::toArray() çıktısıyla sınırlı. Ham modelin bir alanına bağımlıysanız resource'u güncelleyin.

2026-04-21 — v13.4.0

Minor sürüm — Güvenlik sertleştirme sprinti

Paralel bir kod inceleme turu ~37 bulgu çıkardı — 13 HIGH, 14 MEDIUM, 4 LOW. Bu sürümde 36 tanesi kapatıldı; 1 HIGH (git history'deki Passport private-key rotation'ı) operatörün manuel adım atması gereken bir düzeltme. Patch'lerin büyük çoğunluğu publish edilmiş dosyalara (yani sk:install'ın sizin uygulamanıza kopyaladığı dosyalara) dokunuyor; bu yüzden mevcut consumer projeler docs/UPGRADE.tr.md içindeki diff'leri uygulamalı. Taze kurulumlar her şeyi otomatik alıyor. Nadir paket-katmanı değişiklikleri (HSTS preload, stub güncellemeleri) composer update lvntr/laravel-starter-kit ile geliyor.

Security

  • UserPolicy::delete'te self-delete engellendi + API UserController::destroy null guard. UserPolicy::delete actor === target durumunda true dönüyordu, bu nedenle users.delete izni taşıyan herhangi bir authenticated user DELETE /api/v1/users/{self} ile kendini silebiliyordu. Self branch'i artık false dönüyor — kendi kendini silmenin desteklenen tek yolu Profile sayfasındaki password-confirmed Fortify akışı. Api\UserController::destroy ayrıca $request->user() null olduğunda (stale / expired bearer) temiz bir 401 dönüyor — önceki (string) null = '' cast'i boş performer id log'luyordu.

  • CreateRoleAction + UpdateRoleAction role + permission sync'ini DB::transaction içinde çalıştırıyor. Role::create(...) ardından ->syncPermissions(...) transaction dışında koşuyordu; iki write arasında permission-cache race veya bağlantı düşmesi olursa role satırı permission'sız kalıyordu. İki action da artık DB::transaction(...) içinde çalışıyor; RoleCreated / RoleUpdated commit sonrası dispatch ediliyor, listener'lar tutarlı state görüyor.

  • UpdateAuthSettingsAction 2FA revoke loop'unu DB::transaction'a aldı. Admin auth.two_factor'ı off'a çevirdiğinde action önce ayar satırını yazıyor, sonra her user'da two_factor_secret / two_factor_recovery_codes / two_factor_confirmed_at alanlarını temizliyor. Loop ortasında bir fail, sistemi yarı-revoke durumunda bırakıyordu — ayar "2FA off" diyordu ama bazı user'ların aktif TOTP secret'ı hâlâ duruyordu. Tüm operasyon artık atomik.

  • LogoutUserAction null-safe token revoke. API logout endpoint'i $user->token()->revoke() çağırıyordu; request controller'a aktif access token'sız ulaştığında (stale token, cache temizliği, worker race) zincirli çağrı Error: Call to a member function revoke() on null fırlatıp endpoint 500 dönüyordu. Artık ?->revoke() kullanılıyor — token yokken bile temiz 204 dönüyor.

  • FileManager subtree walk'ları N sorgudan 1'e indi. BulkDeleteAction::collectDescendantIds ve DeleteFolderAction::collectDescendantIds silinen klasörün alt ağacını yürürken hop başına bir FileFolder::find sorgusu atıyordu — 50 seviyelik ağaç 50 sıralı sorgu demekti, sibling sayısı arttıkça maliyet büyüyordu ve saldırganlara açık bir request-timing DoS kulbu veriyordu. İki action da artık owner-scoped (id, parent_id) map'ini tek select ile yükleyip ağacı PHP'de visited-set cycle guard ile yürüyor.

  • SMTP encryption=none artık TLS'i doğru devre dışı bırakıyor. Publish edilen Mail ayarları ekranı "No encryption" seçeneği sunuyordu ama SettingsServiceProvider 'none' string'ini config('mail.mailers.smtp.encryption')'a olduğu gibi yazıyordu. Laravel'in SMTP transport'u null dışındaki her değeri — 'none' dahil — "bu TLS modunu kullan" olarak yorumluyor, yani kayıtlı "No encryption" ayarları ilk connect'te default STARTTLS upgrade'ine düşüyor ve offer etmeyen sunucularda fail edebiliyordu. Provider artık outbound config write'ında 'none' → null eşlemesi yapıyor.

  • ApiExceptionHandler — exception mesajı sızıntısı + X-Request-ID log injection. Exception→status mapping'in default arm'ı config('app.debug') ? $e->getMessage() : 'A server error occurred.' dönüyordu; APP_DEBUG'ın yanlışlıkla açık kaldığı her ortamda handle edilmemiş exception'lar API tüketicilerine stack-trace-grade detay sızdırıyordu. Handler artık generic mesajı koşulsuz dönüyor; debug detayı sadece Log::error'a ve zaten APP_DEBUG ile gated olan debug block'una yazılıyor. Trace id artık her zaman Str::uuid() ile sunucu tarafında üretiliyor; client'tan gelen X-Request-ID header'ı yalnızca charset + length-cap sanitizer'dan ([A-Za-z0-9._-], ≤128 char) geçtikten sonra correlation metadata olarak kabul ediliyor ve client_request_id olarak log'lanıyor — kötü niyetli bir client artık uygulama log'una CRLF payload veya sahte trace id enjekte edemiyor.

  • SecurityHeaders HSTS direktifine preload eklendi. Baseline HSTS header'ı max-age=31536000; includeSubDomains'ten max-age=31536000; includeSubDomains; preload'a çevrildi; deployment artık HSTS preload listesi için uygun. Paket src/ katmanından geliyor — composer update ile otomatik.

  • Parola politikası 10+ / mixed case / digits / symbols seviyesine çıkarıldı. AppServiceProvider artık proje-wide bir Password::defaults(...) kuruyor; default'a güvenen her FormRequest (register, password reset, password confirm, profile password change) otomatik devralıyor. Mevcut user'ların parolası invalidate olmuyor — yalnızca yeni parolalar daha sert kurala karşı ölçülüyor.

  • Axios CSRF + credential default'ları. resources/js/app.ts artık axios.defaults.withCredentials = true, xsrfCookieName = 'XSRF-TOKEN', xsrfHeaderName = 'X-XSRF-TOKEN' + X-Requested-With: XMLHttpRequest + Accept: application/json set ediyor. Admin UI Fortify endpoint'lerini (2FA, sessions, password-confirm) Axios üzerinden doğrudan çağırıyor; withCredentials ve XSRF header olmadan tarayıcı session cookie'sini gönderiyordu ama mutating request'lerde CSRF token'ı göndermiyordu — yani compromised bir origin, web flow'unun güvendiği CSRF check'ini bypass edebiliyordu.

  • 2FA QR kodu v-html yerine <img src="data:image/svg+xml;base64,..."> ile render ediliyor. Fortify QR kodunu SVG string olarak döner. Önceki v-html="qrCodeSvg" çalışıyordu ama man-in-the-middle (veya bozulmuş bir Fortify override'ı) SVG'ye <script> / onload iliştirirse çalıştırırdı. Yeni yaklaşım SVG'yi base64'lü <img> data URL'ine çeviriyor — <img> sandbox'ı inline script'leri SVG içinde olsa dahi çalıştırmaz.

  • useDefinition.load() / loadAll() fail eden fetch'te loaded.value = true yapmıyor. Composable datatable / form option dropdown'larını besleyen definition JSON'ının tek durak yükleyicisi. Daha önce .then(r => r.json()) zincirini doğrudan kuruyordu — fetch fail ederse (network, 500, parse) loaded.value true kalıyor ve UI hiçbir konsol geri bildirimi olmadan stale / boş option listesi göstermeye devam ediyordu. İki metod da artık try/catch içinde, res.ok kontrolü var, hatalar konsola yazılıyor ve fail'da loaded.value false kalıyor böylece consumer retry edebiliyor.

  • On bir FormRequest::authorize(): return true; ihlali kapatıldı. Şu request'ler — admin user store, API user store, admin role store, admin settings (auth/general/mail/storage/filemanager/turnstile), test-mail, destroy-sessions — artık authorize()'ı ilgili *.create / *.update permission check'ine delege ediyor (destroy-sessions sadece $this->user() !== null kontrolü yapıyor). CheckResourcePermission middleware'i zaten bunları route seviyesinde zorluyordu ama kontrolü request'e taşımak, controller action'ının off-route çağrıldığı (test, internal dispatch) veya action map'in yeni route isimleriyle drift ettiği anda açılacak defense-in-depth boşluğunu kapatıyor. Public auth endpoint'leri (Api/Auth/*Request) ve FileManager context-based request'leri bilinçli olarak dokunulmadı.

  • 2FA challenge artık kesinlikle tek kullanımlık. TwoFactorChallengeAction yanlış TOTP / yanlış recovery code / boş submit durumunda api:2fa_challenge:{uuid} cache entry'sini bırakıyordu; geçerli bir challenge id'yi ele geçirmiş saldırgan 5 dakikalık TTL × throttle:5/min penceresinin tümünü kod denemesi için kullanabiliyordu. Her fail arm'ı artık Cache::forget($cacheKey) çağırıyor — challenge id kesinlikle bir kez çalışıyor; sonraki denemeler invalidChallenge()'a düşüyor ve client yeni bir uuid almak için tekrar login olmak zorunda.

  • SettingService::getValue / getGroup allGrouped() cache'inden okuyor + setGroup() DB::transaction'da. Sıcak okuma yolu, full allGrouped() sonucu için zaten bir cache katmanı olmasına rağmen çağrı başına bir sorgu atıyordu. Settings-yoğun request path'leri (Dashboard, FileManager, Admin sayfaları) request başına birkaç round-trip kazandı. Bulk write path'i de artık atomik — multi-setting save sırasında bir fail, DB'yi karışık durumda bırakmıyor.

  • MoveItemRequest — item_type'a göre item_id tiplendirmesi. Kurallar her item_type için her item_id değerini kabul ediyordu. Effective kural artık item_type=file için integer|min:1, item_type=folder için uuid — DB şemasıyla birebir uyumlu; item_type'ın kendisi string|in:... string formu yerine Rule::in([...]) kullanıyor.

  • DeleteFolderRequest — explicit FormRequest, çıplak Request yerine. FileManagerController::deleteFolder önceden ham Request alıyor, context'i controller içinde kuruyor ve authorizer'ı doğrudan çağırıyordu. Yeni DeleteFolderRequest, FileManagerRequest'i extend ediyor, paylaşılan context kurallarını çalıştırıyor ve $request->context() expose ediyor — diğer FileManager endpoint'leriyle aynı yüzey; controller iki satır boilerplate düşürdü.

  • UserController::uploadAvatar artık explicit Gate::authorize('update', $user) çalıştırıyor. UploadAvatarRequest::authorize() zaten {user} route parametresi bind'li olduğunda UserPolicy::update'e delege ediyor ama controller'daki ikinci Gate çağrısı view/update/delete'de kullanılan belt-and-braces pattern'ini yansıtıyor ve controller'ı yalnız başına okurken kontrolü görünür tutuyor.

Security — manuel operatör adımı

  • GV-H1 — Passport private keys rotation. .gitignore kuralı düşmeden önce bu dosyaları commit etmiş legacy install'larda storage/oauth-private.key ve storage/oauth-public.key git history'de duruyor. docs/UPGRADE.tr.md §6 git filter-repo + passport:keys --force + passport:purge + takım-geneli git reset --hard akışını belgeliyor; bu adım paket tarafında otomatize edilemez. Repo'nuz key dosyalarını hiç commit etmediyse adımı atlayın.

Changed

  • LOG_LEVEL default'u artık error. .env.example daha önce LOG_LEVEL=debug shipping ediyordu — production'da (verbatim commit edilirse) log'u SQL trace, Passport token debug'ı vb. ile dolduruyor, gürültülü ve zaman zaman hassas. Production profili error veya warning göndermeli.

  • laravel/tinker require-dev'e taşındı. Tinker geliştirici kolaylığı — production dependency olarak shipping edilmesi PsySH'ı ve transitive zincirini her container build'e çekiyordu. Local dev require-dev'de olduğu için yine kuruyor.

  • .env.example Passport key + Turnstile placeholder'ları kazandı. İki yorumlanmış PASSPORT_PRIVATE_KEY / PASSPORT_PUBLIC_KEY stub'ı env-based key-loading path'ini (önerilen alternatif, storage/oauth-*.key commit etmek yerine) belgeliyor; uncommented TURNSTILE_ENABLED=false + boş site/secret key'ler taze install'larda Turnstile middleware'ini admin açana kadar no-op yapıyor.

  • Inertia appEnv / appDebug shared prop'ları artık production'da sızmıyor. HandleInertiaRequests::share önceden config('app.env') + config('app.debug') koşulsuz dönüyordu. Production'da bu, ortam adını her authenticated user'a sızdırıyor ve APP_DEBUG'ın açık olup olmadığını ilan ediyordu. İki anahtar da artık app()->environment('production') altında null / false dönüyor; non-prod'da dev overlay için gerçek değeri taşıyor.

  • CORS preflight cache'i 0'dan 7200 saniyeye çıkarıldı. config/cors.php önceden max_age => 0 shipping ediyordu, her mutating request'te tarayıcıya preflight rerun ettiriyordu. max_age=7200 ile SPA / mobile client'lar OPTIONS cevabını 2 saat cache'liyor.

Fixed

  • useDialog / useImageLightbox — 300 ms timer leak. İki composable da close()'ta 300 ms setTimeout başlatıyordu; exit animasyonu oynasın diye DOM'dan kaldırma işi geciktiriliyordu. Hızlı open → close → open sekansı iki timer kuyruğa alabiliyordu; sondaki, dialog yeniden açıldıktan sonra fire ediyor ve render'ı iptal ediyordu. Module-seviyesi timer ref'i artık hem open() hem close() girişinde temizleniyor; timeout gövdesi fire ettiğinde ref'i null'luyor.

  • SkForm dirty-form guard — parent prop update'leri user input'unu silmiyor. watch(derivedDefaults, ...) block'u parent her yeni object gönderdiğinde form'u koşulsuz default'lara reset ediyordu. User form'u yarı doldurmuşken parent poll etse (örn. sibling datatable refresh paylaşılan-state update tetiklerse), yazılmakta olan input siliniyordu. Watcher artık internalForm.isDirty'i kontrol ediyor — form dirty ise yeni değerler default olarak kaydediliyor (sonraki bir reset() onları alsın diye) ama canlı form state'i korunuyor.

  • SkDatatable URL filtreleri — api.get + Promise.allSettled. URL-driven filter loader çıplak fetch(...) + Promise.all kullanıyordu, yani bir filtrenin options endpoint'indeki tek bir 500, handle edilmemiş rejection ile tüm filter bar'ını zehirliyordu. Loader artık paylaşılan api.get<T>() helper'ını kullanıyor (Axios default'larını + XSRF'i devralıyor) ve Promise.allSettled kullanıyor; her filtre bağımsız, fail eden endpoint boş listeye düşüyor ve konsola warning basılıyor. Aynı dosyada let activeMenuItems → const activeMenuItems (ref hiç re-assign edilmiyordu).

  • TwoFactorTab.enableTwoFactor Inertia reload'u await ediyor. Orijinal kod router.reload({ only: [...] })'u await etmeden fire ediyor, sonra hemen loadQrAndSetupKey()'e geçiyordu. Yavaş bağlantıda QR fetch reload'la race edip stale ekran render edebiliyordu. router.reload artık onFinish'te resolve olan bir promise'e sarılı.

  • ProfileInfoTab / UserForm — as any avatar cast'leri kaldırıldı. İki (x as any)?.avatar_url erişimi tiplendirilmiş shape ile değiştirildi — davranış değişikliği yok ama cast, backing tip avatar_url accessor'ını kaybettiği anda ortaya çıkacak gerçek bir TypeScript hatasını gizliyordu.

  • DashboardController::index explicit : Response return type aldı. Proje Larastan seviyesinde kalan son return_type_missing bulgusunu kapatıyor.

Yükseltme

composer update lvntr/laravel-starter-kit --with-all-dependencies yalnızca paket src/ katmanını (HSTS preload, stub güncellemeleri) alıyor. Yukarıdaki diğer her fix publish / stub-backed dosyalarda yaşıyor. Tam diff listesi ve smoke-test checklist'i için docs/UPGRADE.tr.md.

2026-04-20 — v13.3.3

Patch sürüm — Builder core importları için Windows build düzeltmesi

Fixed

  • Windows production build Could not load .../FormBuilder/core hatasıyla patlıyordu. FormBuilder, DatatableBuilder ve TabBuilder bileşenlerinin her biri, index.ts'i @lvntr/components/<Builder>/core olarak import edilen bir core/ klasörüne sahip. Bazı Windows kurulumlarında Vite resolver'ı dizin→index.ts adımını atlayıp vite:load-fallback'e düşüyor, klasörü dosya gibi okumaya çalışıp ENOENT fırlatıyordu. Düzeltme: her üç builder için core/ klasörünün yanına, ./core/index'ten re-export yapan bir core.ts barrel dosyası eklendi; böylece import her platformda gerçek bir dosyaya rezolve oluyor. macOS/Linux davranışı değişmedi, /core/builder gibi mevcut subpath importları da etkilenmedi. Fixes lvntrdev/laravel-starter-kit#1.

2026-04-19 — v13.3.2

Patch sürüm — güvenlik sertleştirmesi, user audit event'leri, Logo API zarfı, media-delete policy, permission-middleware cache doğruluğu, test bootstrap

Tam test suite auditi sırasında ortaya çıkan gizli bug'ların toplu düzeltmesine ek olarak, admin user flow'undaki bir privilege-escalation yolunu kapatan, Settings ekranının SMTP/S3/Turnstile sırlarını frontend'e sızdırmasını durduran ve API auth akışını web akışıyla aynı seviyeye (email verification + iki-adımlı doğrulama) çeken bir güvenlik incelemesi yapıldı. Orijinal bug'ların çoğu sadece belirli runtime'larda (Octane/queue worker) veya site:install atlanmış taze clone'larda görünen, ya da user write'ları için audit log'u sessizce düşüren sorunlardı.

Security

  • Rol atamasında privilege escalation — admin user flow. StoreUserRequest ve UpdateUserRequest eskiden role alanını yalnızca Rule::exists('roles', 'name') ile doğruluyordu; yani users.create veya users.update izni olan herhangi bir kullanıcı, admin UI dropdown'ının sunduğundan bağımsız olarak ham HTTP isteğiyle role=system_admin gönderebiliyordu — Gate::before üzerinden tüm yetki kapılarını bypass eden super-admin rolüne anında atlıyordu. Ayrıca UpdateUserRequest hedef kullanıcının rank'ine bakmıyordu; bu nedenle düşük rank'lı bir actor, kendinden üstün rank'lı birini (örn. system_admin) edit edip düşürebiliyordu. Düzeltme: role artık Rule::in(...) ile doğrulanıyor — liste, dropdown'ı besleyen hiyerarşi-farkında RoleSelectOptionsQuery tarafından üretiliyor (sort_order >= actor'ın min sort_order, system_admin non-system_admin actor'lara kapalı). UpdateUserRequest::authorize() da hedefin top-rank'i actor'un rank'inden yüksek olduğunda 403 dönüyor. Rolü olmayan ama Spatie direct permission ile users.* taşıyan bir actor, mümkün olan en düşük rank olarak muamele görür — hiçbir rol atayamaz ve kendisinden başkasını edit edemez; önceki (int) null = 0 fallback'i kazara system_admin dahil tüm rol listesini açıyordu.

  • Settings sırları artık frontend'e sızmıyor. Admin Settings sayfası mail.password, storage.spaces_secret, storage.aws_secret ve turnstile.secret_key değerlerini, settings.read izni olan her kullanıcıya Inertia prop olarak düz metin gönderiyordu. Yalnızca .env'de duran değerler bile config() fallback'i üzerinden sızıyordu. Düzeltme: SettingsDefaultsQuery her secret alan için null dönüyor ve yanında *_is_set: bool flag'i ekliyor. Admin UI değer set olduğunda •••••••• placeholder'ı gösterip, form boş submit edildiğinde sıfırdan boş string yolluyor — backend bunu "mevcut değeri koru" olarak yorumluyor; boş olmayan değer yazılırsa üstüne yazar. Yeni tests/Feature/Admin/Settings/SecretsDisclosureTest Inertia payload'ının ham secret string'ini hiçbir yerde taşımadığını doğruluyor.

  • storage.aws_secret artık DB'de şifreli saklanıyor. config/settings.php içindeki sensitive_keys listesine storage.aws_secret eklendi — daha önce mail.password, storage.spaces_secret ve turnstile.secret_key listede vardı ama AWS muadili yoktu, UI üzerinden kaydedilen S3 secret'ları settings tablosunda plaintext duruyordu. SettingService listedeki her anahtarı yazarken Crypt::encryptString ile şifreliyor, okurken çözüyor.

  • check.permission middleware'i production'da fail-closed. Middleware, route'tan çözülen permission (örn. users.index için users.read) DB'de seed edilmemişse isteği geçiriyordu. Production'da bu, permission kaydı unutulmuş her yeni rotayı sessizce korumasız bırakmak anlamına geliyordu. Middleware artık app()->environment('production') altında çalışırken AuthorizationException (403) fırlatıyor, non-production ortamlarda ise seed edilmemiş permission'ı Log::warning ile kaydediyor — dev ergonomisi korundu, production foot-gun'ı kapatıldı.

  • Test-mail endpoint'i artık ham exception detayını yansıtmıyor. SettingsController::testMail() eskiden SMTP exception mesajını (host / username / TLS detayları) tarayıcıya flash ediyordu. Mesaj artık Log::error'a sınıf + message context'iyle yazılıyor; kullanıcı yalnızca generic bir "Failed to send test email. Check the server logs for details." görüyor — aynı başarı/başarısızlık sinyali, bilgi ifşası olmadan.

  • API auth — email verification ve iki-adımlı doğrulama web flow'uyla paritede. API eski durumda register ve her başarılı parola login'inde hemen access token veriyordu, web flow'unun zorunlu kıldığı email-verification ve 2FA checkpoint'lerini bypass ediyordu. Üç POST /api/v1/auth/* endpoint'i yeniden düzenlendi:

    • register — Fortify'ın emailVerification feature'ı açıkken (default), register'da token verilmiyor. Endpoint kullanıcıyı oluşturuyor, Illuminate\Auth\Events\Registered fırlatıyor (Fortify'ın notification pipeline'ı verification link'ini gönderiyor) ve { data: { user, requires_verification: true } } + 201 dönüyor. Feature kapalıysa eski token-on-register davranışı korunuyor.
    • login — discriminated payload dönüyor:
      • { user, token } — normal başarı
      • { requires_verification: true } — credential'lar geçerli ama email verify edilmemiş (verification feature açıkken)
      • { requires_two_factor: true, challenge: "<uuid>" } — credential'lar geçerli ama hesapta 2FA confirmed; tek kullanımlık bir challenge id veriliyor (5 dakikalık cache TTL). Henüz access token yok.
    • two-factor-challenge — yeni endpoint POST /api/v1/auth/two-factor-challenge (throttle 5/dk). TOTP için { challenge, code } veya { challenge, recovery_code } kabul ediyor. Başarıda { user, token } dönüyor. TOTP Fortify'ın TwoFactorAuthenticationProvider'ı ile doğrulanıyor; recovery code'lar hash_equals ile eşleştirilip replaceRecoveryCode üzerinden tüketiliyor, böylece yeniden kullanılamıyorlar. Geçersiz / bilinmeyen / süresi dolmuş challenge'lar 401 dönüyor.

    API tüketicileri için breaking — register / login'dan gelen her 2xx yanıtta { user, token } bekleyen client'ler artık data.requires_verification ve data.requires_two_factor flag'lerine göre dallanmalı ve hesapta 2FA onaylıysa token almadan önce /api/v1/auth/two-factor-challenge endpoint'ini tamamlamalıdır. 2FA'sız, verify edilmiş kullanıcılar eski şekli görmeye devam ediyor.

  • Settings required validation'ı UI secret göstergesiyle uyumlu. UpdateMailSettingsRequest ve UpdateTurnstileSettingsRequest, bir secret'ın "zaten set" olup olmadığına karar verirken yalnızca DB kaydına bakıyordu; eğer değer sadece .env'de duruyorsa UI'daki *_is_set flag'i true dönüyordu (çünkü SettingsDefaultsQuery config()'e fallback yapıyor) ama password / secret_key alanı boş bırakılmış form submit edilince kafa karıştırıcı bir required hatası veriyordu. required branch'i artık query ile aynı mantığı izliyor — DB satırı VEYA config fallback — yani env-destekli kurulumlar artık bu hatayı görmüyor.

  • Admin avatar upload / delete'te IDOR. POST /users/{user}/avatar ve DELETE /users/{user}/avatar rotaları CheckResourcePermission altında herhangi bir permission'a map'lenmiyordu — çünkü route action'ları uploadAvatar / deleteAvatar, middleware'in ACTION_ABILITY_MAP tablosunda yoktu; middleware permission kontrolü olmadan $next($request) dönüyordu. UploadAvatarRequest::authorize() de koşulsuz true dönüyordu. Bu kombinasyonla, yalnızca dashboard.read taşıyan bir user rolü bile — email verified şartıyla — herhangi bir kullanıcının (sistem admini dahil) avatar'ını üzerine yazabiliyor veya silebiliyordu. Düzeltme: action map'ine uploadAvatar => update ve deleteAvatar => update eklendi; UploadAvatarRequest::authorize() route'ta {user} parametresi varsa UserPolicy::update'e delege ediyor (profile self-upload akışı aynı kalıyor); SettingsController::deleteAvatar açık olarak Gate::authorize('update', $user) çağırıyor.

  • Admin UserController ve API UserController: view / update / delete için rank-hiyerarşisi guard'ı. GET /users/{user}/data, GET /users/{user}/edit, DELETE /users/{user}, PATCH /api/v1/users/{user} ve DELETE /api/v1/users/{user} yalnızca users.read / users.update / users.delete permission kontrolüne ve (yalnızca admin UI'daki) UpdateUserRequest::authorize() rank check'ine güveniyordu. Permission taşıyan düşük rütbeli bir admin, data endpoint veya API üzerinden yüksek rütbeli bir kullanıcıyı hâlâ okuyabiliyor veya silebiliyordu. Düzeltme: UserPolicy::view / update / delete artık aynı canManage() rank kontrolünü çalıştırıyor (system_admin bypass, rolsüz actor'lar en düşük rütbe sayılıyor). Admin ve API controller'ları her cross-user operasyonda Gate::authorize('view' / 'update' / 'delete', $user) çağırıyor. Admin ve API UpdateUserRequest'ler authorize()'ı UserPolicy::update'e delege ediyor; böylece rank kontrolü tüm akışlarda aynı.

  • POST /api-routes/regenerate-docs herhangi bir authenticated kullanıcı tarafından çağrılabiliyordu. Route action'ı regenerateDocs da ACTION_ABILITY_MAP'te yoktu, bu yüzden CheckResourcePermission permission kontrolsüz $next($request) dönüyordu. Email verified her authenticated kullanıcı, sunucuda artisan komut çalıştıran OpenAPI regeneration'ı tetikleyebiliyordu. Düzeltme: regenerateDocs => update map'e eklendi; config/permission-resources.php'ye api-routes.update eklendi; seeder permission kaydını oluşturuyor.

  • Logo + FileManager'da SVG upload yasaklandı. Hem admin logo uploader (SettingsController::uploadLogo) hem de FileManager default MIME listesi image/svg+xml'i kabul ediyor ve dosyayı public disk'e kaydediyordu. SVG <script>, onload ve foreignObject JavaScript'i gömebilir; mağdur direkt /storage/... URL'sini açtığında script app origin'de çalışır (stored XSS). Düzeltme: logo validation artık mimes:png,jpg,jpeg,webp + dimensions:max_width=4096,max_height=4096 sabitliyor. UploadFileRequest içine BLOCKED_MIMES listesi (image/svg+xml, image/svg, text/html, application/xhtml+xml) eklendi — file_manager.accepted_mimes'te ne yazılı olursa olsun effective liste bu değerlerden arındırılıyor. UpdateFileManagerSettingsRequest kayıt sırasında bu MIME'ları Rule::notIn(...) + ^[a-z0-9.+-]+/[a-z0-9.+-]+$ regex ile reddediyor. Admin UI picker'ları (MimePickerField, FileManagerTab, GeneralTab logo input) artık SVG listelemiyor. SettingsDefaultsQuery::fileManager() eski install'lardaki seed'lenmiş SVG'yi UI'a göndermeden önce süzüyor; böylece geçmiş kurulumlar formda SVG'yi seçili görmüyor.

  • Avatar rule'u sertleştirildi. UploadAvatarRequest::rules() eskiden ['required','image','max:2048'] idi — image rule'u SVG'yi kabul ediyor ve piksel boyutu için sınır koymuyordu, bu da polyglot dosyalar ve decompression-bomb PNG'ler için kapı açıktı. Yeni rule: required | image | mimes:jpg,jpeg,png,webp | max:2048 | dimensions:max_width=4096,max_height=4096.

  • media-library.disk_name artık default local. Önceki default public idi — installer seeder başarısız olsa, admin FileManager disk toggle'ını değiştirse veya seeder atlansa, kullanıcının yüklediği belgeler dünya-okunabilir URL üzerinden servis ediliyordu. Default artık local olduğu için eksik konfigürasyon fail-closed; FileManager zaten indirmeleri DownloadFileAction üzerinden stream ediyor, public URL'e ihtiyacı yok.

  • SESSION_ENCRYPT + SESSION_SECURE_COOKIE default true. config/session.php içinde 'encrypt' => env('SESSION_ENCRYPT', false) ve 'secure' => env('SESSION_SECURE_COOKIE') (null default) vardı. Bu env değişkenlerini set etmeyi unutan deploy'lar, HTTPS üzerinde bile plaintext session payload + secure bayraksız cookie gönderirdi. İki default de artık true; .env.example zaten ikisini true olarak shipping ediyor ve Herd HTTPS'le serve ettiği için local dev de etkilenmiyor.

  • SecurityHeaders middleware'i baseline CSP header ekliyor. Middleware X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy / HSTS ekliyordu ama Content-Security-Policy yoktu. Kodbase'de iki v-html sink'i olduğu için (Fortify 2FA QR SVG ve DataTable column.render escape hatch) CSP saldırı alanını anlamlı ölçüde daraltıyor. Header yalnızca non-local ortamlarda set ediliyor — local dev'deki Vite HMR, script/connect/style için dev-server origin'ine ihtiyaç duyuyor ve bu değer geliştiriciye göre değişiyor, bu yüzden local'de tight CSP dev akışını bozmaktan başka işe yaramaz.

  • Scramble "Try It" production'da kapalı. config/scramble.php hide_try_it: false + try_it_credentials_policy: 'include' ile shipping ediliyordu — production'da api-docs.read taşıyan herhangi bir admin'e, kendi session cookie'lerini her isteğe iliştiren in-browser API tester'ı sunuyordu. İki değer de artık APP_ENV === 'production' kontrolüne göre ayrılıyor (prod'da gizli + omit, local/staging'de interaktif).

  • Passport access-token TTL kısaltıldı, scope kataloğu eklendi. Access token'lar 15 gün, personal access token'lar 6 ay geçerli tutuluyordu — sızan bir bearer token haftalarca kullanılabilir kalıyordu. Yeni default'lar: access_token_minutes=60, refresh_token_days=14, personal_token_days=30; eski PASSPORT_TOKEN_DAYS / PASSPORT_PERSONAL_TOKEN_MONTHS env anahtarları set edilmişse yine öncelikli, bu yüzden mevcut kurulumlar etkilenmiyor. config/starter-kit.php artık opt-in bir scope kataloğu (users.read, users.write, files.read, files.write, admin) shipping ediyor; Passport::tokensCan() önceden bağlı — spesifik API rotalarına middleware('scope:...') eklediğin anda per-scope erişim devreye giriyor.

  • API register / login artık turnstile middleware'ini çalıştırıyor. Cloudflare Turnstile tarayıcı auth formları için FortifyServiceProvider + ValidateTurnstile üzerinden bağlıydı, ama API rotaları (POST /api/v1/auth/register, POST /api/v1/auth/login) yalnızca throttle:5,1 limit'i taşıyordu — saldırgan dakika başına IP başına beş hesap açabiliyordu. İki rota da artık mevcut turnstile middleware alias'ından geçiyor; Turnstile settings'te kapalıysa middleware no-op, açıldığında API de aynı cf_turnstile_response kontrolünü devralıyor.

Fixed

  • User domain event'leri artık Create/Update/Delete'te dispatch ediliyor. App\Domain\User\Actions\CreateUserAction, UpdateUserAction ve DeleteUserAction içindeki UserCreated::dispatch(...) / UserUpdated::dispatch(...) çağrıları daha önce yorumda veya eksikti — DomainServiceProvider'da register edilen listener'lar (audit-log listener dahil) user write'ları için hiç çalışmıyordu. Create ve Update artık gerçekten değişiklik olduğunda dispatch ediyor (no-op update UserUpdated fırlatmıyor); Delete ise silmeden önce id/email yakalayıp başarı halinde UserDeleted fırlatıyor — Role* action pattern'iyle aynı.

  • Admin users.show rotası 500 dönsüyordu. routes/web/user-route.php içindeki Route::resource('users', UserController::class) örtük olarak GET /users/{user} rotasını açıyordu ama UserController hiç show() metodu taşımıyordu — o URL'ye her istek BadMethodCallException fırlatıyordu. Resource kaydı artık ->except(['show']) ile daraltıldı; detay verisi admin UI'nın zaten kullandığı GET /users/{user}/data endpoint'inden okunuyor.

  • Settings logo endpoint'leri artık ApiResponse zarfı dönüyor. App\Http\Controllers\Admin\SettingsController içindeki POST /settings/logo ve DELETE /settings/logo eskiden çıplak response()->json([...]) / response()->json(status: 204) dönsüyordu — admin API'nın geri kalanının izlediği "her JSON yanıt { success, status, message, data } zarfı taşır" sözleşmesini kırıyordu. Her ikisi de artık to_api(...) üzerinden geçiyor. Frontend (GeneralTab.vue) json.data.logo_url okuyor, şekil aynı.

  • App\Policies\UserPolicy'ye delete ability'si eklendi. DELETE /media/{media} MediaUploadController'da Gate::authorize('delete', $media->model) çağırıyor. Medyanın sahibi bir User ise, UserPolicy'de delete tanımlı olmadığı için (sadece view ve update vardı) Gate fallback'ten deny'a düşüyor ve 403 dönüyordu — kendi avatar/dosyasını silmeye çalışan sahip için bile. Yeni delete(User $actor, User $user) metodu update'i birebir yansıtıyor: self her zaman izinli, aksi halde actor'un users.delete permission'ına ihtiyacı var.

  • CheckResourcePermission middleware: process-geneli cache yerine request-scoped cache. Middleware içindeki permission-existence lookup'ı sonucu static $cached değişkeninde tutuyordu. Uzun ömürlü worker'larda (Laravel Octane, container'ı job'lar arası sıcak tutan queue worker'lar) bu cache hiç yenilenmiyordu — yeni oluşturulan permission kayıtları worker restart edene kadar görünmez kalıyordu. Daha kötüsü, test suite içinde static test'ler arası hayatta kalıyordu: RefreshDatabase permissions tablosunu truncate ediyordu ama middleware önceki test'in seed ettiği permission isimlerini hâlâ "var" olarak raporluyor ve permission'sız olması gereken rotalarda aralıklı 403'ler üretiyordu. Cache artık app()->instance('check-permission.cache', ...) ile saklanıyor — prod'da request-scoped, test container'ında test-scoped.

  • UserFactory two_factor_* kolonlarını default olarak null seed ediyor. Eloquent strict mode (Model::shouldBeStrict(! isProduction()), Lvntr\StarterKit\StarterKitServiceProvider tarafından set ediliyor), kod taze bir factory instance üzerinde o kolonları okuduğunda (örn. $this->actingAs(User::factory()->create()) sonrası ProfileController) "attribute [two_factor_secret] either does not exist or was not retrieved" fırlatıyordu. Factory artık two_factor_secret, two_factor_recovery_codes ve two_factor_confirmed_at için açık null yazıyor — in-memory model ->refresh() gerekmeden üçünü de taşıyor.

  • CreateUserAction ve UpdateUserAction artık write + role sync'i transaction içinde çalıştırıyor. User::create(...) ardından ->syncRoles(...) transaction dışında koşuyordu — syncRoles başarısız olursa (bağlantı düşmesi, permission cache invalidation, role-not-found race) user row'u kalıyor ama rol atanmıyordu, admin listede tutarsız kayıt görünüyordu. İki action da artık DB::transaction(...) içinde çalışıyor; event dispatch commit'ten sonra yapılıyor, böylece listener'lar tutarlı state görüyor.

  • MoveItemAction::wouldCreateCycle artık her ancestor için SELECT atmıyor. Method folder ağacını FileFolder::find($parentId) ile her hop'ta tek tek yürüyordu; N ancestor'ı olan bir klasör taşındığında N sorgu atılıyordu. Büyük ağaçlarda hem performans ayak tuzağı hem de slow-query DoS için potansiyel yoldu. Ancestor haritası artık tek sorguda yükleniyor (SELECT id, parent_id WHERE owner_type=? AND owner_id=?) ve walk in-memory, cycle-visited guard'ıyla yapılıyor.

  • Folder create / rename / move artık unique-constraint ihlallerini yakalıyor. CreateFolderAction, RenameFolderAction ve MoveItemAction (owner_type, owner_id, parent_id, name) için check-then-act yapıyordu. Eş zamanlı iki istek exists kontrolünü eş anlı geçince, ikincisi temiz validation hatası yerine ham QueryException (500) veriyordu. Race pencere artık kapalı — her action SQL-state 23000 (veya MySQL 1062) yakalayıp localized LogicException fırlatıyor; controller'lar bunu zaten 422 + sk-file-manager.errors.duplicate_folder mesajına çeviriyor. Mevcut pre-check parent_id=NULL senaryosunu (MySQL/SQLite NULL'ı farklı saydığı için unique index korumuyor) korumak üzere duruyor.

  • UserDatatableQuery artık media relation'ını eager load ediyor. UserResource::$appends avatar_url accessor'ını zorluyor, o da $user->getFirstMedia('avatar') çağırıyor. Datatable query yalnızca roles eager load ettiği için her satır ayrı bir media lookup tetikliyordu (N+1). media artık eager load listesinde; per-page render 1 + n sorgudan 2'ye düştü.

  • RoleController@data ve @edit artık $role->toArray() spread yerine RoleResource kullanıyor. Spread eklemesi hızlı olmuştu ama projenin "response'lar bir Resource üzerinden geçer" konvansiyonunu kırıyor ve roles tablosuna eklenecek gelecekteki herhangi bir hassas kolonu otomatik yayınlayacaktı. Yeni App\Http\Resources\Admin\Role\RoleResource alanları açıkça listeliyor (id, name, display_name, group, sort_order, guard_name, seeded_permissions, timestamps + permissions yüklendiğinde conditional). Frontend payload şekli korundu.

  • resources/js/pages/Admin/ApiRoutes/Index.vue: external link'e rel="noopener noreferrer" eklendi. "Open API Docs" anchor'ı target="_blank" kullanıyor ama rel attribute'u eksikti. Projenin geri kalanıyla tutarlı hale getirildi.

  • 2FA disable confirmation dialog'u için eksik çeviriler. sk-setting.auth.two_factor_disable_title ve sk-setting.auth.two_factor_disable_warning anahtarları Auth settings tab'ından referans ediliyordu ama lang dosyalarında tanımlı değildi. EN ve TR için eklendi.

Added

  • API test suite'i için Passport key otomatik üretimi. tests/Pest.php artık tests/Feature/Api scope'una bir beforeEach hook'u kaydediyor — storage/oauth-private.key eksikse passport:keys --force çalıştırıyor. Taze clone ve CI runner'lar artık Passport-destekli testler (AuthTest, UserTest) geçsin diye php artisan site:install çalıştırmak zorunda değil — eski davranış league/oauth2-server tarafından atılan anlaşılmaz bir LogicException: Invalid key supplied idi.

  • tests/Feature/Domain/User/UserEventsTest.php. Yukarıdaki fix'in getirdiği event-dispatch sözleşmesini kilitler — UserCreated create'te dispatch ediliyor, UserUpdated sadece takip edilen en az bir alan değiştiğinde fırlıyor, UserDeleted başarılı silmede fırlıyor, self-delete guard false dispatch üretmiyor.

  • tests/Feature/Admin/SettingsTest.php'ye logo upload/delete coverage'ı eklendi. POST /settings/logo üzerinde ApiResponse zarfını (200 + data.logo_url) ve DELETE /settings/logo üzerinde 204 sözleşmesini kilitliyor.

2026-04-18 — v13.3.0

Özellik sürümü — Cloudflare Turnstile, last-login takibi, dosya önizleme modalları, shipping edilen validation.php ve sk-* çeviri namespace'i

Geniş bir sürüm. Birkaç bağımsız yeni özellik ve çeviri katmanında mimari bir değişiklik.

Eklenen

  • Auth akışlarında Cloudflare Turnstile captcha. Login, register ve şifre sıfırlama formlarında artık bir Turnstile widget'ı (resources/js/components/Auth/TurnstileWidget.vue) render ediliyor ve token sunucu tarafında doğrulanıyor. Shipping: turnstile middleware alias'ı (App\Http\Middleware\ValidateTurnstile), ad-hoc validasyon için App\Rules\TurnstileRule, App\Domain\Setting\DTOs\TurnstileSettingsDTO, ve Ayarlar → Turnstile admin sekmesi (site key / secret key UI üzerinden yönetiliyor). Kurulum bazlı açılıp kapatılıyor; feature kapalıysa widget'lar temiz şekilde short-circuit olur.

  • Last-login takibi. Yeni App\Listeners\UpdateLastLogin listener'ı Illuminate\Auth\Events\Login'e bağlı: her başarılı girişte user'a last_login_at ve last_login_ip yazılıyor. Kullanıcı detay sayfasında ve users datatable'ında sıralanabilir kolon olarak görünür.

  • Girişte pasif kullanıcı engeli. App\Providers\FortifyServiceProvider artık authenticate olan kullanıcının status'ü active değilse login attempt'i reddediyor ve net bir hata dönüyor — session başlamıyor. Bir hesabı askıya almak için artık silmek gerekmiyor.

  • FormBuilder.trans(bool). Her field builder'a eklenen yeni fluent method (FB.inputText(), FB.select(), FB.toggleSwitch(), …). Label'ın çeviri anahtarı olarak mı (varsayılan, true) yoksa önceden çevrilmiş raw string olarak mı (false) render edileceğini belirler. Script içinde trans('admin.example') gibi çevrilmiş bir değer vermek istediğinde işe yarar — normalde form template'i $t()'i tekrar çağırdığı için anahtar bulunamaz, fallback'e düşer. .trans(false) ile template ikinci çeviri adımını atlar. Varsayılan davranış değişmedi; mevcut sayfalar hiç dokunmadan çalışmaya devam eder.

    FB.inputText().key('last_name'); // varsayılan — label → $t('validation.attributes.last_name')
    FB.inputText().key('x').label(trans('admin.example')).trans(false); // raw render, ikinci $t() çağrısı yok
    
  • Uygulama içi dosya önizlemeleri (lightbox + modal). Yüklenen dosyalar — file manager'da ve her FB.fileUpload() form alanında — thumbnail'a veya dosya adına tıklandığında artık yeni tarayıcı sekmesi açmıyor. Resimler tam ekran lightbox'ta açılıyor (Google Drive tarzı: bulanıklaştırılmış koyu arkaplan, ESC ile kapanır, isim sol üstte). Resim olmayan dosyalar (PDF, video, audio, text) mime-bazlı dialog'ta açılıyor; dialog doğru viewer'ı embed ediyor (iframe / <video> / <audio>), file manager tarafında "İndir" butonu ve tanınmayan formatlar için "Yeni sekmede aç" escape hatch'i var. Lightbox tek bir global overlay — AdminLayout'ta <AppDialog /> yanına register ediliyor; modal ise mevcut useDialog composable üzerinden açılan FilePreviewModal component'i.

  • Dosya Yöneticisi ayarlarında kategorize mime-type seçici. Ayarlar → Dosya Yöneticisi → Kabul edilen dosya türleri eskiden uzun bir multiselect dropdown'du. Artık kategorize kart-checkbox ızgarası (Görseller / Dokümanlar / Arşiv) — her seçenek eşleşen dosya tipi ikonuyla birlikte. Dropdown listesinden daha kolay taranıyor, tıklama alanı tüm kart, alfabetik sıra yerine mantıklı gruplama var.

  • "Video yükleme" ve "Ses yükleme" için feature-toggle kartları. Dosya Yöneticisi ayarlarındaki iki toggle, mime picker ile aynı kart estetiğini paylaşıyor — solda renkli ikon, ortada kalın başlık + kısa açıklama (örn. "MP4, WebM, MOV, MKV, AVI ve OGG video formatlarına izin ver."), sağda switch. Kartın herhangi bir yerine tıklamak toggle'ı çevirir.

  • lang/{en,tr}/validation.php artık kit ile shipping ediliyor. Laravel'in default validation rule mesajları + hem Laravel validator'ının hem de FormBuilder / DatatableBuilder'ın kullandığı attributes ve custom bölümleri. .label() belirtilmediğinde FormBuilder ve DatatableBuilder, alan etiketini validation.attributes.{key} üzerinden otomatik çözer. Türkçe mesajlar Laravel-Lang/lang konvansiyonlarını takip ediyor. Tüketici uygulamalar bu dosyaları serbestçe düzenleyip yeni attribute label'ları ekleyebilir — özel bir translation loader'a ihtiyaç yok, her şey Laravel'in native translation sistemi üzerinden çalışıyor.

  • Rol ismi lokalizasyonu — zarif bir fallback zinciri ile. Admin topbar / sidebar'da görünen (Inertia üzerinden auth.role olarak paylaşılan) rol etiketi artık üç adımda çözülüyor: önce roles.display_name[locale] veritabanından; sonra config('permission-resources.display_names.roles.{name}.{locale}') altındaki locale anahtarı; son olarak da Str::headline($role->name) — yani taze seed edilmiş system_admin gibi bir rol, hiçbir lokalize tanım yapılmamış olsa bile raw slug yerine "System Admin" olarak görünüyor.

Değişen — çeviriler sk-* namespace'ine taşındı

Shipping edilen her çeviri dosyası artık sk- dosya adı prefix'i taşıyor: sk-admin.php, sk-auth.php, sk-button.php, sk-datatable.php, sk-menu.php, sk-setting.php, sk-user.php, sk-attribute.php, sk-file-manager.php, sk-activity-log.php, … Shipping edilen tüm Vue sayfaları ve PHP kodu yeni anahtarları kullanıyor (__('sk-button.save'), eski __('button.save') yerine). Amaç: tüketici uygulamalar prefix'siz namespace'i özgürce sahiplensin (örn. lang/en/admin.php'yi starter kit menü metinleriyle çarpışmadan kendi dashboard string'leri için kullansın).

Kaldırılan

13.3 öncesi prefix'siz stub'lar — stubs/lang/{en,tr}/{admin,auth,button,common,datatable,enums,file-manager,message,pagination,passwords,validation}.php (21 dosya) — artık shipping edilmiyor. sk-* geçişinden sonra kit içinde hiçbir kod bunlara referans vermiyordu; taze kurulumlarda tutulması sadece kafa karıştırıyordu. Paket seviyesindeki starter-kit:: namespace'i dokunulmamış — __('starter-kit::admin.menu') çağrıları hâlâ çalışıyor.

Düzeltilen

  • "Video yükleme" açık olsa bile .ogg video ve .avi dosyaları reddediliyordu. Upload request'in allow_video=true branch'i yalnızca video/mp4, video/webm, video/quicktime ve video/x-matroska mime'larını whitelist'liyordu. video/ogg, video/x-msvideo ve video/avi eklendi; validation hata mesajlarındaki "İzinli tipler" listesine .OGV ve .AVI uzantı etiketleri de eklendi.

  • npm run build üzerindeki gereksiz uyarılar susturuldu. Production build'den iki gürültülü uyarı temizlendi: (1) @tailwindcss/vite ve @inertiajs/vite tarafından basılan "Sourcemap is likely to be incorrect" uyarıları — iki plugin de transform'dan sonra sourcemap'i yeniden üretmiyor, runtime etkilenmiyor — artık vite.config.ts içindeki odaklı bir Rollup onwarn hook'u ile filtreleniyor (diğer uyarılar olduğu gibi geçmeye devam ediyor); (2) shipping edilen SkDatatable.vue ve FileManager.vue üzerinde çıkan resolveDirective imported but never used uyarısı — PrimeVue'nun v-tooltip / v-ripple direktifleri artık <script setup> bloğunda açıkça binding ediliyor (const vTooltip = Tooltip) ve template dinamik bir lookup yerine doğrudan referansa derleniyor.

13.2.x'ten yükseltme

sk:update hash-aware çalışır: dokunmadığın dosyalar yeni sürümle değiştirilir; düzenlediğin dosyalar skipped veya untracked olarak raporlanır ve dokunulmaz. 13.3'ün birkaç özellik dosyası — SettingsController, SettingsDefaultsQuery, FortifyServiceProvider, HandleInertiaRequests, AppServiceProvider ve yeni FormRequest sınıfları — büyük ihtimalle bu listede görünecek ve ilgilenilmesi gerekecek.

  1. Önce neyin skip/untracked olduğuna bak: php artisan sk:update --dry-run

  2. app/ katmanında lokal özelleştirmen yoksa tüm dosyalar için paket sürümünü kabul et:

    php artisan sk:update --force
    
  3. Yeni çeviri dosyalarını elle kopyala (sk:update lang/'a dokunmaz):

    cp vendor/lvntr/laravel-starter-kit/stubs/lang/en/sk-*.php lang/en/
    cp vendor/lvntr/laravel-starter-kit/stubs/lang/tr/sk-*.php lang/tr/
    
  4. lang/en/ altında önceki sk:install'dan kalma admin.php, auth.php, … varsa artık öksüzler. Paket onlara referans vermiyor; kendi __('admin.x') çağrılarını __('sk-admin.x')'e taşıdıktan sonra silebilirsin.

  5. npm run build — yeni TurnstileWidget.vue shipping edilen bir stub ve Login/Register/ForgotPassword tarafından import ediliyor. Taze kurulumlar otomatik alır. Dosyayı henüz almamış mevcut kurulumlarda build şu hatayla patlıyor: Could not load resources/js/components/Auth/TurnstileWidget.vue; sk:update dosyayı kopyalamış olmalı (mevcut dosya değil, yeni dosya), kopyalamamışsa şuradan al: vendor/lvntr/laravel-starter-kit/stubs/resources/js/components/Auth/TurnstileWidget.vue.


2026-04-16 — v13.2.9

npm run build — lang JSON çift import uyarısı giderildi

Tüketici projelerde npm run build her seferinde şu iki uyarıyı basıyordu:

(!) lang/php_en.json is dynamically imported by resources/js/app.ts but also statically imported by resources/js/app.ts, dynamic import will not move module into another chunk.
(!) lang/php_tr.json is dynamically imported ...

Sebep: resources/js/app.ts içindeki i18nVue resolve callback'i SSR ve client için iki ayrı import.meta.glob('../../lang/*.json', ...) çağrısı tutuyordu — biri eager: true (statik), diğeri normal (dinamik). Vite iki dalı da statik analiz ediyor, aynı dosyalar için hem statik hem dinamik import gördüğü için "dinamik dal ayrı chunk'a alınmayacak" diyordu. Dinamik dal aslında hiçbir kazanç sağlamıyordu çünkü dosyalar zaten statik bundle'daydı.

Tek eager glob'a indirildi, modül scope'una çıkarıldı, client'ta Promise.resolve() ile sarmalandı:

const langs = import.meta.glob<Record<string, string>>('../../lang/*.json', { eager: true });
const resolveLang = (lang: string) => langs[`../../lang/php_${lang}.json`];
app.use(i18nVue, {
    resolve: ssr ? resolveLang : (lang: string) => Promise.resolve(resolveLang(lang)),
});

Lang JSON dosyaları küçük (birkaç KB) olduğu için statik bundling'in bundle boyutuna etkisi sıfıra yakın — uyarı kalıcı olarak kaybolurken davranış aynı kalıyor.


2026-04-16 — v13.2.8

Daha temiz ilk kurulumlar

Yeni kurulumlar artık gereksiz geliştirme kalıntıları ve gürültülü örnek veriler taşımıyor.

  • .env.example temizliği — tekrarlanan DB_* satırları ve eski örnek veritabanı adı kaldırıldı. Dosya artık yalnızca your_database, your_username gibi genel placeholder'ları tutuyor.
  • Frontend/kurulum temizliği — gerekli olmayan geliştirme odaklı frontend tooling kayıtları çıkarıldı; böylece npm install daha temiz bir başlangıç yapıyor.
  • Daha az proje karmaşası — yeni uygulamaya ait olmaması gereken yardımcı/tooling dosyaları artık gönderilmiyor.

2026-04-15 — v13.2.7

File manager upload — HTTP context'i için crypto.randomUUID fallback'i

File manager upload composable'ı, kuyruğa giren her dosya için crypto.randomUUID() ile geçici bir id üretiyordu. Bu API yalnızca secure context'te (HTTPS ya da localhost) tanımlı — dolayısıyla düz HTTP bir dev domain'inde çalışan tüketiciler (Herd'in .test'i, çıplak bir intranet IP'si vs.) TypeError: crypto.randomUUID is not a function alıyor ve upload ilk XHR atılmadan ölüyordu.

useFileManager artık üç kademeli fallback'e sahip lokal bir generateTempId() helper'ından geçiyor:

  1. Varsa crypto.randomUUID() (HTTPS / localhost)
  2. crypto.getRandomValues(new Uint8Array(16)) hex olarak seri hale getirilmiş (her modern tarayıcıda var, secure-context gerektirmez)
  3. Son çare olarak Date.now().toString(16) + Math.random().toString(16)

tempId yalnızca bir pending-upload satırını tamamlanma/hata callback'iyle eşleştirmek için kullanılıyor — kriptografik güce ihtiyaç yok, bu yüzden fallback güvenli.

Güvenlik başlıkları — geolocation kendi kaynağından izinli

SecurityHeaders middleware'indeki Permissions-Policy geolocation=() idi (tamamen reddediliyordu). geolocation=(self) olarak değiştirildi; böylece first-party script'ler meşru bir ihtiyaç olduğunda geolocation isteyebiliyor — üçüncü taraf frame'ler hâlâ bloklanıyor.


2026-04-15 — v13.2.6

File manager validation mesajları — okunabilir, lokalize, dosya adıyla

File manager'da sunucu reddi durumunda toast'lar artık gerçekten görünüyor ve Laravel'in ham files.0 field must be a file of type: image/webp mesajı yerine anlamlı bir Türkçe mesaj taşıyor.

  • Toast group bug fix — FileManager.vue içindeki tüm toast.add() çağrılarına group: 'bc' eklendi. Ortak ToastComponent group="bc" ile mount edildiği için bu anahtar olmadan gönderilen toast'lar sessizce düşüyordu. Klasör oluştur/yeniden adlandır/sil/taşı ve dosya yükleme (başarı + hata) toast'ları artık tekrar görünüyor.
  • Sunucu hata mesajı çıkarımı — Upload XHR önceden 422 cevabında sadece envelope.message ("Validation error.") okuyordu. Composable artık envelope.errors'u dolaşıp ilk alan-bazlı mesajı çıkarıyor; toast asıl gerekçeyi taşıyor.
  • Dosya başına anlamlı validation mesajı — UploadFileRequest attributes() ve messages() metodlarını override ediyor. Her files.{i} slot'u dosyanın getClientOriginalName()'ine bağlı (toast files.0 yerine vacation.jpg yüklenemedi: … diyor). Mimetypes / max-size hataları okunabilir uzantı listesi (İzinli tipler: WEBP, PDF, JPG, …) ve insanca boyut limiti (en fazla 10 MB) ile çevrildi.
  • Çeviri anahtarları — errors.upload_invalid_type, errors.upload_too_large, errors.upload_invalid_file lang/{en,tr}/file-manager.php'ye eklendi.

İki yeni feature testi mesajları kapsıyor: orijinal dosya adı kontrolü ve okunabilir boyut limiti. File manager + install + publish suite'leri tamamen yeşil (22/22 + 11/11).

Helpers yeniden organize — vendor-owned core, user-owned custom, publishable override

to_api() ve format_date() (artı iki yeni helper — aşağıda) artık paket vendor'undan geliyor ve otomatik autoload ediliyor. Son kullanıcı uygulamaları to_api kopyasını app/ altında tutmuyor — bu da her sk:update'te ortaya çıkan merge baş ağrısını ortadan kaldırıyor.

  • vendor/lvntr/laravel-starter-kit/src/sk-helpers.php kanonik konum. Paketin composer.json autoload.files'ı üzerinden register ediliyor; composer require ile birlikte helper'lar anında geliyor.
  • app/Helpers/custom.php son kullanıcı uygulamasına ilk install'da basılıyor, app'in composer.json autoload.files'ına ekleniyor ve sk:update ile asla üzerine yazılmıyor. Kullanıcının kendi global helper'ları buraya yazılır.
  • app/helpers.php deprecated. sk:update mevcut dosyanın md5'ini bilinen stock hash listesiyle karşılaştırıyor; eşleşirse dosya sessizce siliniyor. Kullanıcı kendi fonksiyonlarını eklediyse dosya konsol uyarısıyla yerinde bırakılıyor — kullanıcı kodu korunuyor. composer.json autoload entry'si yalnızca dosya gerçekten silinince yeniden yazılıyor; sessizce kullanıcı kodu kırılmıyor.
  • İki yeni helper — definition($key, $value) DefinitionService'ten eşleşen definition kaydını (object) döndürüyor; definitionLabel($key, $value) onun label'ını döndürüyor. Enum-style değerleri görüntülenecek string'e çevirirken her çağrıda definition listesini tekrar çekmeden işe yarıyor.

sk:publish --tag=helpers — paket helper'larını fork etmeden override et

Yeni bir tag sk-helpers.php'yi publish komutuna açıyor. Publish sonrası dosya app/Helpers/sk-helpers.php'ye iniyor, kullanıcı serbestçe düzenleyebiliyor.

Vendor dosyası autoload anında published kopyayı tespit edip require_once ile route ediyor:

$skPublishedHelpers = dirname(__DIR__, 4).'/app/Helpers/sk-helpers.php';
if (is_file($skPublishedHelpers) && realpath($skPublishedHelpers) !== realpath(__FILE__)) {
    require_once $skPublishedHelpers;

    return;
}

Realpath guard, dosya published kopya olarak yüklendiğinde self-recursion'ı engelliyor. composer.json değişikliğine gerek yok — composer autoload yine vendor dosyasını tetikliyor, o da kullanıcı dosyasına delegate ediyor. Published dosya silindiğinde anında vendor implementasyonuna geri dönülüyor.

sk:publish interaktif prompt'una dördüncü seçenek geldi: Global Helpers (sk-helpers.php).


2026-04-14 — v13.2.4

Tip güvenliği turu — vue-tsc ve ESLint sıfır uyarı

Starter kit kaynak kodu artık vue-tsc --noEmit ve eslint 'resources/js/**/*.{ts,vue}' altında 0 hata / 0 uyarı ile geçiyor. Davranışta değişiklik yok, tamamen tip ve lint temizliği.

  • tsconfig tekilleştirme — tip tarama yolları sadeleştirildi; aynı UI kaynakları artık iki kez taranmıyor. Böylece lokal geliştirmede kafa karıştıran duplicate hatalar kaldırıldı.
  • Vite Components plugin tek kaynak — dirs artık yalnızca resources/js/components tarıyor; paket yolu kaldırıldı. Auto-generated components.d.ts artık source yollarına referans veriyor.
  • SkDatatable filter tipleri genişletildi — activeFilters tipi string | number | Date | (Date | null)[] | null ile tek FilterValue alias'ı üzerine oturtuldu. DatePicker kullanımları v-model → :model-value + @update:model-value ile güvenli cast'lere dönüştürüldü; select, select-button, date, daterange filtrelerinin her biri kendi tipinde çalışıyor.
  • Tag icon / pagination i18n fix'leri — :icon ifadesi ?? undefined ile null sızmasını kapatıyor, datatable.records_info çevirisine geçilen from/to/total parametreleri artık String(... ?? 0) ile zaten beklenen string tipine uyuyor.
  • SharedPageProps index signature — PageProps constraint'ini karşılayacak şekilde [key: string]: unknown eklendi. useCan() artık usePage<SharedPageProps>() generic'iyle temiz derleniyor.
  • env.d.ts auth şekli gerçekle hizalandı — Inertia sharedPageProps.auth artık { user, role, role_names, permissions } tutuyor; AdminHeader'daki page.props.auth?.role okuması ve benzerleri doğru tiplerle resolve oluyor. appEnv, appDebug, locale, availableLocales da tiplenmiş şekilde shared prop'larda.
  • Küçük prop / cast düzeltmeleri — RoleForm.vue Wayfinder update.url({ id }) şeklinde çağrılıyor (optional id'yi narrowing ile geçiyor), Settings/Index.vue general tipine logo_url: string | null eklendi, Dashboard/Index.vue selamlamada user?.name yerine mevcut alan olan user?.first_name kullanıyor, Inertia v3'te zaten default olan preserveScroll: true opsiyonu router.reload() çağrılarından kaldırıldı.
  • ESLint uyarıları — SkDatatable içindeki v-html gerekçeli disable-next-line yorumuyla işaretlendi (render string'i author-tanımlı ve escapeHtml helper'ı sunuluyor). Breadcrumb.rootLabel, FileGrid.emptyLabel ve SkTag.{value,icon,color,severity} prop'larına withDefaults ile default değerler verildi.

Mevcut uygulamalar için aksiyon gerekmiyor — değişiklikler davranışsal değil, tip/lint düzeyinde.

2026-04-14 — v13.2.3

Installer DX — AST tabanlı enjeksiyon, bootstrap helper, preset uyarıları

Fresh bir Laravel üzerinde composer require lvntr/laravel-starter-kit akışını daha güvenli ve daha az invaziv hale getiren installer/upgrade ergonomi turu.

  • AST tabanlı config enjeksiyonu — sk:install artık config/app.php, config/filesystems.php ve config/media-library.php'yi nikic/php-parser ile format-preserving pretty print kullanarak düzenliyor. Regex tabanlı patch kaldırıldı; farklı Laravel config formatlarına toleranslı ve tamamen idempotent (bir kere inject edildikten sonra sk:install'ı tekrar çalıştırmak no-op).
  • Bootstrap helper sadeleştirmesi — middleware ve exception wiring'i tek bir ortak bootstrap helper üzerinden akıyor; bu da kurulum ve güncelleme davranışını daha öngörülebilir hale getiriyor.
  • bootstrap/app.php artık ezilmiyor — stub kopyası kaldırıldı. Bunun yerine installer kullanıcının mevcut Laravel default dosyasına sadece üç satır AST-inject ediyor: withRouting(...) içine api: __DIR__.'/../routes/api.php', withMiddleware / withExceptions closure'larına ise Bootstrap::middleware(...) / Bootstrap::exceptions(...) çağrıları. Kullanıcının eklediği middleware, trusted proxies, custom exception reporter vs. korunuyor.
  • bootstrap/providers.php artık ezilmiyor — installer array'e DomainServiceProvider, FortifyServiceProvider, SettingsServiceProvider ekliyor (idempotent, zaten kayıtlı olanları atlıyor); kullanıcının mevcut provider'larına dokunmuyor.
  • package.json JSON-merge — blind overwrite yerine akıllı merge: ortak dependency'lerde stub versiyonu kazanıyor, kullanıcının eklediği dep/script/workspace/root-level key'ler korunuyor.
  • Lang dosyaları için first-install tespiti — lang/* re-install'da hâlâ preservable (customization kaybolmaz), ama gerçek ilk install'da (hash registry yok) installer artık force-copy yapıyor; böylece fresh projeler Laravel'in cılız default lang dosyalarıyla kalmıyor ve starter kit UI eksik çeviri göstermiyor.
  • Ölü kod temizliği — aktif akışın parçası olmayan eski IdentityType ve YesNo enum'ları yeni kurulumlardan ve güncelleme akışından çıkarıldı.
  • IdeHelper temizliği — yeni kurulumlarda AppServiceProvider artık gereksiz class_exists(IdeHelperServiceProvider::class) kontrolünü taşımıyor.
  • Açık nikic/php-parser ^5.0 bağımlılığı — Tinker üzerinden dolaylı yoldan zaten kuruluydu, şimdi pakete direkt dep olarak eklendi.
  • "Bare Laravel" kurulum uyarısı — README (EN/TR) ve install.md / install.tr.md en üstte uyarıyla açılıyor: starter kit'ten önce install:inertia, install:api, Breeze, Jetstream veya benzeri preset'leri çalıştırmayın — preset'ler starter kit'in de yayınladığı controller/route/sayfa/layout'ları oluşturur, installer bunları tespit edemez ve yetim "ölü kod" olarak kalırlar.
  • Testler — 12 yeni InstallCommandTest senaryosu: AST config enjeksiyonu (üç dosya için), idempotency, format/yorum koruma, package.json merge, first-install tespiti, bootstrap app/providers AST enjeksiyonu + user-code koruma. Toplam installer test suite 20/20 yeşil.

Mevcut kurulumlar için aksiyon gerekmiyor — installer tarafındaki tüm değişiklikler geriye dönük uyumlu, first-install tespiti ya da idempotent guard'larla korunuyor.

2026-04-14 — v13.2.2

FileManager — ContextRegistry ile pluggable context'ler

FileManager artık user / global ile sınırlı değil. Her Eloquent model'i klasör ağacına sahip olabilir; service provider'a tek satır bile yazmadan.

  • Yeni ContextRegistry servisi (app/Domain/FileManager/Support/) bir context anahtarını üç adımda çözer: explicit register() → Laravel morph-map alias → App\Models\{Studly(key)} convention fallback. Tanımsız anahtar yine validation üzerinden 422 döner.
  • Sıfır-konfig custom context — model class'ı + karşılığında bir policy (view / update) yeterli:
    <FileManager context="vehicle" :context-id="vehicle.id" height="100%" />
    
  • global registry içine gömüldü — önceden AppServiceProvider::boot()'ta duran kayıt artık ContextRegistry constructor'ında. Starter kit'i adopt ederken FileManager için boot-time kurulum gerekmez. AppServiceProvider sadece singleton binding yapıyor.
  • user tamamen auto-resolve — App\Models\User convention + yeni paketle gelen app/Policies/UserPolicy.php (self + users.read / users.update). user için explicit kayıt kaldırıldı, yetki policy'ye taşındı.
  • Self-match kısa yollu default authorizer — auto-resolve edilen context'lerde actor kendi kaydına dokunuyorsa (actor IS owner) otomatik izin. Diğer istekler Laravel policy'lerine delegate: okuma can('view', $owner), yazma can('update', $owner).
  • MorphMap uyumu — FileManagerContextDTO artık ownerType'ı $owner->getMorphClass() ile saklıyor; model morph-map alias'ı olsa bile query ve path üretimi tutarlı.
  • Runtime-driven validation — FileManagerRequest sabit in:user,global kuralı yerine çalışma anında ContextRegistry'ye soran bir closure kullanıyor. Yeni context tanımlamak için hiçbir Request dosyası güncellenmiyor; context_id yalnızca kayıtlı path'te {id} varsa zorunlu.
  • Custom key'ler için frontend tip gevşemesi — FileManagerContext artık 'user' | 'global' | (string & {}). <FileManager context="vehicle" /> tamamen tip-güvenli, built-in anahtarlarda autocomplete kaybolmuyor.
  • Upload dayanıklılığı — UploadFileRequest, file_manager.accepted_mimes ayarı seed edilmemiş fresh install'larda makul bir MIME listesine (image / pdf / office / text) fallback ediyor; "file must be of type: ." 422 hatası yok.
  • Testler — yeni CustomContextTest dosyası: explicit register, path override, folder listing, tanımsız-context reject ve morph-map auto-resolve. 26/26 FileManager testi geçiyor.
  • Doküman — file-manager.tr.md "Özel (custom) context'ler" bölümü aldı: çözüm sırası, zero-config walkthrough, VehiclePolicy örneği, contract tablosu ve override rehberi.

2026-04-14 — v13.2.1

FileManager — UX rötuşları ve takip iyileştirmeleri

13.2.0 sürümünün ardından gerçek kullanımdan çıkan bir iyileştirme turu eklendi:

  • Önizleme modalı — dosya tile'ına tek tık veya sağ tık Aç artık 90vw'lik bir modal açıyor; resim, PDF, video, ses ve metin dosyaları inline preview; diğer tipler için "Yeni sekmede aç" + "İndir" aksiyonları.
  • Tile bazlı yükleme progress'i — her dosya ayrı XHR ile yükleniyor; grid'de optimistic placeholder tile üstünde dolan progress bar gösteriliyor. Başarısız yüklemeler dismissable hata tile'ı olarak kalıyor; başarılı olanlar liste yenilendiğinde yerine geçiyor. Toolbar Upload butonu toplu yükleme sırasında spinner'a dönüyor.
  • Drag-and-drop taşıma — tile'lar draggable; bir klasör tile'ına bırakılınca seçili tüm öğeler hedef klasöre taşınıyor. External (OS) dosya sürüklemesi Files data-transfer tipi üzerinden ayırt ediliyor — internal drag artık upload overlay'ini tetiklemiyor.
  • Klasör ağacı picker'lı Move modalı — folder ve file context menülerinde Taşı aksiyonu; açılan dialog'ta FolderTree ile hedef klasör seçiliyor. Tek ve çoklu seçim destekleniyor.
  • Busy overlay (modal kart) — Sil / Taşı / Yeniden Adlandır operasyonlarında FileManager alanının üstüne beyaz modal kart (spinner + başlık + açıklama) çıkıyor; toplu operasyonlarda "N öğe kaldı" canlı sayaç + Durdur butonuyla döngü iptal ediliyor.
  • Her zaman görünür seçim checkbox'ı — her folder/file tile'ının sağ üstünde primary-dolu seçili / outline hover-opaq çıkan checkbox var. Klasörlere çift tıkla aç davranışı geri geldi; tek tık sadece seçer. Dosyalar tek tıkla preview'a gidiyor. Tile üstündeki 3-nokta menüler kaldırıldı — sağ tık tek giriş noktası.
  • Sağ tık artık zorla seçmiyor — seçili olmayan bir tile'a sağ tık mevcut seçimi bozmuyor; bulk aksiyonlar sadece sağ tık yapılan öğe zaten seçimdeyse tetikleniyor.
  • Klavye kısayolları — Ctrl/Cmd + A mevcut klasörün tümünü seçer, Delete / Backspace seçimi siler (confirm'lü), Esc seçimi temizler. Input içindeyken veya dialog açıkken tetiklenmiyor.
  • Breadcrumb yeniden tasarlandı — PrimeVue breadcrumb yerine chip/pill stil crumb'lar, arada chevron, konum info bar'ın altına alındı. Uzun klasör isimleri maxChars (default 18) ile kesiliyor, … ile. Tam ad title tooltip'inde.
  • Başlıkta mevcut klasör + geri butonu — sol folder tree kaldırıldı (sadece Move picker'da kullanılıyor). Ana alan artık klasör ikonu + mevcut klasör adı; root değilken sol tarafta ← butonu.
  • Boş klasör illüstrasyonu — boş klasörlerde büyük outline folder SVG'si + başlık + iki satır ipucu ("Sürükle bırak / Yükle" ve "Yeni Klasör").
  • Info bar'da aggregate istatistik — dosya sayısı + toplam boyut artık mevcut klasörün tüm alt ağacını tarıyor; yalnızca o klasörün direkt dosyalarını değil.
  • Diskler arası download — DownloadFileAction Storage::disk($media->disk)->download(...) kullanıyor; local, S3 ve DigitalOcean Spaces için aynı şekilde çalışıyor.
  • Context menu yeniden stillendi — beyaz rounded kart, daha büyük item padding, folder ve file menülerinde Sil'den önce separator.
  • Sıralama yönü tooltip'i — asc/desc toggle butonunda dinamik PrimeVue tooltip'i ("Artan sıralama · Azaltana geç" / EN). Yan etki olarak Tooltip directive'i app.ts'de global kaydedildi.
  • Footer kredisi — AdminFooter sağda Crafted with Lvntr Starter Kit linki (lvntr.dev'e bağlı).

Güncellenmiş kullanım, prop ve composable çıktıları için file-manager.tr.md.

2026-04-14 — v13.2.0

FileManager — dosya yöneticisi modülü

Yeni bir FileManager modülü eklendi: Windows Explorer tarzı bir UI ile kullanıcı-bazlı veya global dosyalar için tam kapsamlı dosya yönetimi.

  • Nested klasörler — oluştur, yeniden adlandır, taşı, cascade sil
  • Çoklu dosya yükleme — drag & drop veya butonla
  • Seçim — tek tık, Ctrl/Cmd + tık, kauçuk-bant (rubber-band) fare ile toplu seçim
  • Toplu silme — toolbar butonu veya seçili öğeye sağ tık
  • Sıralama — isim / boyut / tarih + asc/desc
  • Türe göre önizleme — resim thumbnail + PDF/Word/Excel/Video/Ses/Arşiv için renk kodlu simgeler
  • Bilgi çubuğu — mevcut klasörün dosya sayısı ve toplam boyutu
  • Context menüler — klasör/dosya/boş alan için ayrı aksiyonlar (Yeni Klasör, Yükle, Tümünü Seç, Yenile)

Eklenen sayfalar: ana menüde Dosyalar, Admin > Kullanıcılar > Düzenle sayfasında Dosyalar tab'ı. Maksimum dosya boyutu, kabul edilen MIME tipleri ve video/ses toggle'ları Admin > Ayarlar > Dosya Yöneticisi altından yapılandırılabilir.

Depolama: user/{id}/files/{uuid}/... ve global/files/{uuid}/... — klasör taşıma tamamen mantıksal, fiziksel dosya hareketi yok.

Kullanım ve API detayları için file-manager.tr.md.

2026-04-13 — v13.1.10

FormBuilder — stale form reset düzeltmesi

FB ile oluşturulan formlarda, Inertia back() navigasyonu veya formConfig'in yeniden hesaplanmasına yol açan herhangi bir page.props tazelenmesi sonrasında formun sessizce eski (stale) remote data'ya resetlenmesine yol açan hata düzeltildi. İç SkForm artık yeni türetilmiş default değerleri öncekilerle shallow-karşılaştırır ve değerler aynıysa reset'i atlar — böylece kullanıcının devam eden düzenlemeleri korunur.

Etkilenen: config'i page.props'a bağımlı olan tüm FB formları (örn. koşullu isFieldsLocked, isSelf, auth'a göre alan görünürlüğü). API değişikliği yok — mevcut formlar otomatik olarak yararlanır.

2026-04-13 — v13.1.8

FormBuilder — ColorSelector çıktı formatı

FB.colorSelector() artık .format() ve .defaultTone() ile yapılandırılabilir çıktı formatlarını destekliyor:

  • format('name') (varsayılan) → "blue" kaydeder
  • format('name-tone') → "blue-500" kaydeder
  • format('hex') → "#3b82f6" kaydeder

'name-tone' ve 'hex' formatlarında dropdown'un altında tıklanabilir bir tone seçici çıkar; seçilen değer tone pill'lerinin yanında gösterilir. Modele başlangıçta bir hex string geldiğinde, component Tailwind paletinde ters arama yaparak eşleşen renk + tone seçimini geri yükler.

Detaylar için formbuilder.tr.md.