jskelet 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +2 -0
  3. package/docs/02-mimari.md +1 -0
  4. package/docs/04-render-ve-sablonlar.md +9 -3
  5. package/docs/06-cache.md +53 -9
  6. package/docs/07-yapilandirma.md +1208 -1191
  7. package/docs/08-build.md +2 -1
  8. package/docs/10-dagitim.md +16 -6
  9. package/docs/12-panel-ve-oturum.md +2 -1
  10. package/docs/en/02-architecture.md +2 -1
  11. package/docs/en/04-rendering.md +9 -3
  12. package/docs/en/06-caching.md +54 -9
  13. package/docs/en/07-configuration.md +24 -9
  14. package/docs/en/08-build.md +2 -1
  15. package/docs/en/10-deployment.md +17 -6
  16. package/docs/en/12-dashboards-and-sessions.md +2 -1
  17. package/package.json +1 -1
  18. package/src/config/defaults.js +541 -518
  19. package/src/config/index.js +1500 -1456
  20. package/src/init.mjs +2 -0
  21. package/src/server/cache-blob.js +70 -0
  22. package/src/server/cache-control.js +45 -0
  23. package/src/server/data-cache.js +118 -27
  24. package/src/server/disk-cache.js +233 -0
  25. package/src/server/html-cache.js +90 -16
  26. package/src/server/image-optimizer.js +95 -2
  27. package/src/server/logs/file-sink.js +159 -32
  28. package/src/server/logs/pipeline.js +10 -3
  29. package/src/server/middleware/static-precompressed.js +31 -10
  30. package/src/server/og-image.js +17 -4
  31. package/src/server/prewarm.js +25 -1
  32. package/src/server/redis.js +31 -12
  33. package/src/server/render.js +910 -910
  34. package/types/config/defaults.d.ts +21 -1
  35. package/types/config/index.d.ts +14 -0
  36. package/types/server/cache-blob.d.ts +13 -0
  37. package/types/server/cache-control.d.ts +28 -0
  38. package/types/server/data-cache.d.ts +9 -0
  39. package/types/server/disk-cache.d.ts +36 -0
  40. package/types/server/html-cache.d.ts +26 -3
  41. package/types/server/logs/file-sink.d.ts +16 -5
  42. package/types/server/og-image.d.ts +5 -0
  43. package/types/server/redis.d.ts +2 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,24 @@ one is listed under a **Breaking** heading.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ### Changed
12
+
13
+ - Shared cache entries of 1 KB or more are stored as brotli (`JSK\x01`
14
+ prefix) instead of plain JSON. With Redis off, the same body is written
15
+ under `.jskelet/cache/<buildId>/` so a restart can skip the render. The
16
+ in-process cache is unchanged, smaller records stay JSON, and existing
17
+ plain JSON values are still read.
18
+ - Early HTML refresh no longer marks every due page in one second. A pass
19
+ soft-stales at most four entries (soonest expiry first), and the expiry
20
+ warmer refetches them at one request at a time, two per second, instead of
21
+ the classic prewarm rate.
22
+ - The data cache stops growing past 64 MB of stored JSON. A single value
23
+ larger than that is not stored; the caller still receives it. HTML cache
24
+ entries keep the raw body plus only the compressed encoding last requested.
25
+ - In production, precompressed asset `stat` results (hit or miss) stay in
26
+ memory for the process lifetime. The remote image disk cache drops the
27
+ oldest file once `.jskelet/image-cache/` passes 256 MB.
28
+
11
29
  ### Added
12
30
 
13
31
  - `robots.txt` responses gain a trailing JSkelet note that disallows framework
@@ -18,6 +36,20 @@ one is listed under a **Breaking** heading.
18
36
 
19
37
  ### Breaking
20
38
 
39
+ - HTML edge headers
40
+ Cacheable HTML and OG images no longer send `s-maxage`. `route()` writes
41
+ `Cache-Control: public, max-age=0` and
42
+ `CDN-Cache-Control: max-age=<html ttl>, stale-while-revalidate=<cache().staleWhileRevalidate>`
43
+ (default 60; `0` omits the directive). OG images keep their own durations
44
+ (86400 / 604800) on `CDN-Cache-Control`. A layer that only reads
45
+ `Cache-Control` / `s-maxage` (nginx `proxy_cache`) no longer caches this
46
+ HTML. Cloudflare reads `CDN-Cache-Control`.
47
+ - File logs are no longer daily plain-text files. With `logs.file.enabled`,
48
+ lines are sealed as zstd chunks (`jskelet-<time>-<n>.ndjson.zst`) and kept
49
+ for at most 5 minutes; the oldest expired chunk is deleted. `logs.drainLog`
50
+ receives each sealed chunk (`{ body, encoding: "zstd", bytes, lines, at }`)
51
+ so the app can forward it. A throwing hook warns and leaves the site up.
52
+ With the file sink off, `drainLog` still runs and nothing is written to disk.
21
53
  - Dev gate is opt-in
22
54
  `DEV_TOKEN` in the environment no longer locks the site. Require the token
23
55
  only with `devGate: true` or `DEV_GATE=1`. `DEV_GATE=0` turns the gate off
package/README.md CHANGED
@@ -257,6 +257,8 @@ export default {
257
257
  async cache() {
258
258
  return {
259
259
  html: { "/": 3600, "/pricing": 3600 },
260
+ // Edge stale window after the HTML TTL. 0 omits the directive.
261
+ staleWhileRevalidate: 60,
260
262
  query: { "/search": ["q"] }, // only these params enter the cache key
261
263
  prewarm: { enabled: true, max: 50, concurrency: 4 },
262
264
  // redis: { enabled: true, url: process.env.REDIS_URL },
package/docs/02-mimari.md CHANGED
@@ -80,6 +80,7 @@ sebebi var ve yer değiştirmek sessiz bozulmalara yol açıyor.
80
80
  `.br`/`.gz` kopyalar varsa onlar servis edilir (brotli kalite 11); yoksa
81
81
  istek altındaki `static`e düşer ve middleware anında sıkıştırır (kalite 5).
82
82
  Hash'li ve `immutable` bir dosyayı her istekte yeniden sıkıştırmak boşa CPU.
83
+ Üretimde `stat` sonucu (var ya da yok) süreç belleğinde kalır; dev'de kalmaz.
83
84
  - **Admin paneli** (açıksa, `admin().enabled` / `JSKELET_ADMIN`): statikten
84
85
  sonra, body parser ve route'lardan önce. Kendi gövde ayrıştırıcısını taşır;
85
86
  uygulama aynı yolu gölgeleyemez. Kapalıyken modül yüklenmez.
@@ -582,9 +582,15 @@ app.get("/og/custom.png", async (req, res) => {
582
582
  });
583
583
  ```
584
584
 
585
- Varsayılan `Cache-Control`:
586
- `public, max-age=0, s-maxage=86400, stale-while-revalidate=604800`.
587
- `cacheControl` seçeneğiyle ezilir. Çalışan örnek: `examples/blog/routes/35-og.mjs`.
585
+ Varsayılan başlıklar (süreler HTML ayarına bağlı değildir):
586
+
587
+ ```
588
+ Cache-Control: public, max-age=0
589
+ CDN-Cache-Control: max-age=86400, stale-while-revalidate=604800
590
+ ```
591
+
592
+ `cacheControl` seçeneği `Cache-Control`'ü ezer; bu durumda `CDN-Cache-Control`
593
+ yazılmaz. Çalışan örnek: `examples/blog/routes/35-og.mjs`.
588
594
 
589
595
  ## Hook'lar
590
596
 
package/docs/06-cache.md CHANGED
@@ -85,6 +85,9 @@ export default {
85
85
  "/haber/:slug": 300,
86
86
  "/etiket/:slug": 120,
87
87
  },
88
+ // Edge'in taze penceresi bittikten sonra eski HTML'i sunacağı süre.
89
+ // 0 yazılırsa direktif hiç basılmaz.
90
+ staleWhileRevalidate: 60,
88
91
  };
89
92
  },
90
93
  };
@@ -206,9 +209,11 @@ leadMs = min(max(produceMs * 2, 250ms), ttl / 2)
206
209
 
207
210
  Böylece yavaş bir sayfa TTL dolduğu anda hâlâ soğuk render'a düşmez: taze
208
211
  HTML çoğu zaman `expiresAt` gelmeden yazılmış olur. Trafik yoksa bir sweeper
209
- aynı pencerede girdiyi soft-bayatlatır ve ısıtma kuyruğuna alır; `startPrewarm`
210
- ( `PREWARM=0` değilse) kuyruğu HTTP ile boşaltır — klasik `prewarmPaths`
211
- olmasa da.
212
+ aynı pencerede girdiyi soft-bayatlatır ve ısıtma kuyruğuna alır. Bir tur en
213
+ fazla dört girdi işaretler (süresi en yakın dolacak olan önce); kalanlar
214
+ sonraki saniyelere kalır. `startPrewarm` (`PREWARM=0` değilse) kuyruğu HTTP
215
+ ile boşaltır — klasik `prewarmPaths` olmasa da — ama bu boşaltma klasik turun
216
+ `rps: 0` ayarını kullanmaz: aynı anda tek istek, saniyede en fazla iki.
212
217
 
213
218
  Stale penceresinde tazelemenin hatası isteği etkilemez: eski HTML pencere
214
219
  boyunca geçerli kalır ve hata yalnızca loglanır
@@ -225,7 +230,9 @@ güncelleniyor.
225
230
  Store LRU'dur: erişilen girdi sona taşınır, sınır (`cache().maxEntries`,
226
231
  varsayılan 500) aşılınca en eski düşürülür. Config 500'ün üstünü isteyebilir;
227
232
  **800'ü geçemez** — daha yükseği uyarıyla 800'e çekilir. Bunun yanında süreç
228
- içi HTML string + sıkıştırılmış gövde **256 MB**'yi geçemez. Sayı tavanının
233
+ içi HTML string + sıkıştırılmış gövde **256 MB**'yi geçemez. Sıkıştırılmış
234
+ kopya tektir: brotli veya gzip, hangisi son istendiyse. Ham HTML durur.
235
+ Sayı tavanının
229
236
  altında kalan şişman sayfa veya `vary.host` kopyası da bu bütçede LRU ile
230
237
  düşer. Tek sayfa 256 MB'den büyükse saklanmaz; yanıt o istekte yine gider.
231
238
 
@@ -254,12 +261,33 @@ ile değiştirilebilir):
254
261
  Önbelleklenebilir yanıtlarda ayrıca:
255
262
 
256
263
  ```
257
- Cache-Control: public, max-age=0, s-maxage=<revalidate>, stale-while-revalidate=60
264
+ Cache-Control: public, max-age=0
265
+ CDN-Cache-Control: max-age=<html ttl>, stale-while-revalidate=<staleWhileRevalidate>
258
266
  ```
259
267
 
260
- `max-age=0` tarayıcıda saklamayı kapatır, `s-maxage` ara katmanlara (CDN, ters
261
- proxy) süreyi bildirir. Böylece CDN önünde durduğunda aynı tazelik modeli iki
262
- katmanda birlikte çalışır.
268
+ `max-age=0` tarayıcıda saklamayı kapatır. Edge süresi `Cache-Control` içine
269
+ yazılmaz: `s-maxage`, Cloudflare'da `max-age=0` ile birlikte `EXPIRED` üretir.
270
+ Süre `CDN-Cache-Control` üzerindeki `max-age` olur ve mevcut HTML TTL'dir
271
+ (`cache().html` ya da route'un `revalidate` değeri). `s-maxage` hiç yazılmaz;
272
+ ona dönen bir anahtar da yoktur.
273
+
274
+ `stale-while-revalidate`, edge'in taze penceresi bittikten sonra eski HTML'i
275
+ sunacağı süredir. `cache().staleWhileRevalidate` ile gelir; varsayılan `60`.
276
+ `0` yazılırsa direktif hiç basılmaz:
277
+
278
+ ```
279
+ CDN-Cache-Control: max-age=<html ttl>
280
+ ```
281
+
282
+ `must-revalidate`, `proxy-revalidate` ve `no-cache` aynı yanıtta yoktur; bu
283
+ direktifler stale penceresini keser.
284
+
285
+ `X-JSkelet-Cache: STALE` süreç içi HTML önbelleğinin katmanıdır. Edge
286
+ başlığındaki stale penceresinden bağımsızdır.
287
+
288
+ Yalnızca `Cache-Control` / `s-maxage` okuyan bir ara katman (nginx
289
+ `proxy_cache`) bu HTML'i artık önbelleklemez. Cloudflare `CDN-Cache-Control`
290
+ okur.
263
291
 
264
292
  ## Sıkıştırılmış gövdenin saklanması
265
293
 
@@ -369,6 +397,9 @@ Ayrıntılar:
369
397
  yazılır (`haber:tr:v2:${slug}`).
370
398
  - TTL `0` verildiğinde önbellek devre dışı kalır ve `producer` her çağrıda
371
399
  çalışır — bir ayarı geçici olarak kapatmak için yeterli.
400
+ - **Bayt tavanı 64 MB.** Sayı sınırı şişman JSON'u tutmaz; süreç içi gövdeler
401
+ bu tavanı geçemez ve config yükseltemez. Tek değer tavanı aşıyorsa saklanmaz,
402
+ çağıran sonucu yine alır. Tahliye en eski girdiden olur.
372
403
 
373
404
  Yönetim yüzeyi:
374
405
 
@@ -517,7 +548,7 @@ baştan render etmek olur — içerik yine aynı eksik hâliyle döner, ziyaret
517
548
  sadece render süresini öder.
518
549
 
519
550
  Eksik veriyle üretilen çıktı **paylaşılan önbelleklere de sunulmaz**: `degraded`
520
- bir yanıt `public, s-maxage=…` değil `private, no-store` alır. Süreç içi
551
+ bir yanıt `CDN-Cache-Control` değil `private, no-store` alır. Süreç içi
521
552
  önbelleğe yazmama kararını CDN'de geri almak, aynı hatayı bir katman yukarıda
522
553
  tekrarlamak olurdu. Teşhis başlığı (`X-JSkelet-Cache: MISS`) yine yazılır.
523
554
 
@@ -835,6 +866,19 @@ anahtarlar TTL ile ölür — elle temizlik ya da `FLUSHDB` gerekmez. Build
835
866
  bilinçli olarak `buildId` **taşımaz**: deploy sırasında eski ve yeni sürüm yan
836
867
  yana koşuyor ve bir purge ikisine de ulaşmalı.
837
868
 
869
+ 1 KB ve üstü HTML ve veri girdileri düz JSON olarak durmaz: `JSK\x01` önekli
870
+ brotli gövde yazılır (kalite 5, metin modu — yanıt sıkıştırmasıyla aynı ayar).
871
+ L1 yine çözülmüş nesne tutar; sıkıştırma yalnızca L1 miss'ten sonraki
872
+ paylaşımda, yanıtı bekletmeden çalışır. Daha küçük kayıtlar düz JSON kalır,
873
+ eski düz JSON kayıtlar da okunur. zstd kullanılmaz: `node:zlib` içindeki zstd
874
+ 22.15'ten itibaren var, motor ise `>=22`.
875
+
876
+ Redis kapalıyken (ya da bağlanamadığında) aynı gövde `.jskelet/cache/<buildId>/`
877
+ altına dosya olarak yazılır. Süreç yeniden açılınca L1 boştur ama diskteki
878
+ taze kopya render'ı atlatır. Bu tek makine içindir: birden fazla instance aynı
879
+ dizini paylaşmaz, küme için Redis durur. Yeni bir `buildId` eskisinin dizinini
880
+ bir sonraki yazışta siler. `clearHtmlCache()` ve invalidation dosyayı da düşürür.
881
+
838
882
  ### Bilmeniz gereken takaslar
839
883
 
840
884
  - **Kişiye özel çıktı paylaşılmaz.** `storable: false` işaretlenen bir render