jskelet 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@ one is listed under a **Breaking** heading.
10
10
 
11
11
  ### Added
12
12
 
13
+ - Dev overlay Errors tab now lists failed SSR and browser `fetch` calls with
14
+ page path, API URL, optional island name, and expandable response-body
15
+ details (JSON instead of `[object Object]`). Server `console.error` /
16
+ `console.warn` records also carry the current page when they fire during
17
+ render.
18
+ - Visit-driven HTML prewarm (`cache().prewarm.onVisit`): after each public
19
+ cacheable page response, same-origin links in the HTML are warmed in the
20
+ background (document order, `perPage` cap). Mutually exclusive with classic
21
+ prewarm (`max` / `priority` / `rotate` / `hooks.prewarmPaths`, etc.) — mixing
22
+ them fails at config load.
13
23
  - Runtime remote image optimizer: set `images.remote.allowHosts` to proxy
14
24
  allowlisted http(s) images through `/_jskelet/image?url=&w=&q=` as resized
15
25
  webp (disk cache under `.jskelet/image-cache/`). `image()` rewrites matching
package/docs/06-cache.md CHANGED
@@ -1052,19 +1052,68 @@ yönlenen gerçek bir istekle o edge'in önbelleğine giriyor; sunucudan
1052
1052
  cache'lememe kararının en sık sebepleri, ve bu panelde `dynamic` olarak
1053
1053
  görünür.
1054
1054
 
1055
- ## Prewarm — açılışta ısıtma
1055
+ ## Prewarm — açılışta veya ziyarette ısıtma
1056
1056
 
1057
1057
  Next'teki build-time prerender'ın karşılığı, ama çıktı diske yazılmaz: önbellek
1058
- süreç belleğinde yaşadığı için ısıtma da süreç ayağa kalkınca yapılır. Kazanç
1059
- aynı — ilk ziyaretçi soğuk render'ı beklemez — fakat veri dondurulmaz; her girdi
1060
- route'un `revalidate` süresiyle yaşlanır ve stale-while-revalidate ile arkada
1061
- tazelenir.
1058
+ süreç belleğinde yaşadığı için ısıtma da süreç ayağa kalkınca (klasik mod) ya
1059
+ da trafik geldikçe (`onVisit`) yapılır. Kazanç aynı — tıklanan / komşu sayfa
1060
+ soğuk render'ı beklemez — fakat veri dondurulmaz; her girdi route'un
1061
+ `revalidate` süresiyle yaşlanır ve stale-while-revalidate ile arkada tazelenir.
1062
1062
 
1063
1063
  Isıtma **gerçek HTTP istekleriyle** yapılır (`http://127.0.0.1:<port>`), çünkü
1064
1064
  cache anahtarı, sıkıştırma ve middleware zinciri normal trafikle bire bir aynı
1065
1065
  olsun.
1066
1066
 
1067
- ### `hooks.prewarmPaths()`
1067
+ İki mod **karşılıklı dışlayıcıdır**. `cache().prewarm.onVisit` açıksa klasik
1068
+ alanlar (`max`, `priority`, `rotate`, `intervalSeconds`, …) ve
1069
+ `hooks.prewarmPaths` birlikte verilemez — config yüklenirken hata fırlar.
1070
+ Tersi de geçerli: klasik liste ısıtması kullanıyorsanız `onVisit` yazmayın.
1071
+
1072
+ ### Mod: `onVisit` — ziyaret edilen sayfanın linkleri
1073
+
1074
+ Bir kullanıcı herkese açık, önbelleklenebilir bir sayfayı (`public` HTML, 200)
1075
+ aldığında framework yanıt HTML'indeki aynı-origin `<a href>` yollarını (üstten
1076
+ alta, `perPage` kadar) kuyruğa alır ve arka planda ısıtır. Bir sonraki tıklama
1077
+ veya aynı sayfaya gelen başka ziyaretçi çoğu zaman `HIT` görür.
1078
+
1079
+ ```js
1080
+ // jskelet.config.mjs
1081
+ export default {
1082
+ async cache() {
1083
+ return {
1084
+ html: { "/": 60, "/haber/:slug": 300 },
1085
+ prewarm: {
1086
+ onVisit: {
1087
+ perPage: 20, // sayfa başına en fazla link
1088
+ concurrency: 2, // opsiyonel
1089
+ rps: 4, // opsiyonel; 0 = sınırsız
1090
+ },
1091
+ },
1092
+ };
1093
+ },
1094
+ };
1095
+ ```
1096
+
1097
+ `onVisit: true` de yeterlidir (varsayılan `perPage: 20`).
1098
+
1099
+ Kurallar:
1100
+
1101
+ - Yalnızca `route()` ile giden **public + cache'lenebilir** 200 HTML tetikler;
1102
+ `private`, degraded veya `no-store` yanıtlar link çıkarmaz.
1103
+ - Isıtma isteğinin kendi UA'sı (`brand.prewarmUserAgent`) tetiklemez — sonsuz
1104
+ crawl olmaz.
1105
+ - Zaten taze olan yollar kuyruğa girmez.
1106
+ - `nofollow`, `target="_blank"`, `data-no-prefetch`, `prewarmSkip` ve
1107
+ `navigation.exclude` Speculation Rules ile aynı muafiyetleri paylaşır.
1108
+ - Query string ısıtılmaz (varsayılan cache politikası query'yi dinamik sayar).
1109
+ - Açılışta otomatik tur yoktur; ilk ziyaretçi o sayfa için hâlâ MISS
1110
+ ödeyebilir. Kritik yolları deploy öncesi sıcak tutmak istiyorsanız klasik
1111
+ modu veya readiness + seed tercih edin.
1112
+ - `PREWARM=0` onVisit'i de kapatır. `PREWARM_MAX` / `PREWARM_INTERVAL_SECONDS`
1113
+ / `PREWARM_DELAY_MS` / `PREWARM_RETRY_DELAY_MS` onVisit ile birlikte
1114
+ kullanılamaz (hata).
1115
+
1116
+ ### Mod: klasik — `hooks.prewarmPaths()`
1068
1117
 
1069
1118
  Hangi yolların ısıtılacağını uygulama bildirir; genelde sitemap üreten
1070
1119
  fonksiyonun aynısıdır.
@@ -1089,9 +1138,10 @@ Kurallar:
1089
1138
  `/api/`, `/_fragment/`, `/__jskelet/`. Oturuma bağlı sayfalar ısıtılmamalı.
1090
1139
  - Tekilleştirme **sırayı korur**: `priority` verilmediğinde uygulamanın verdiği
1091
1140
  sıra anlamlıdır — en önemli sayfaları başa koyun.
1092
- - Bu hook tanımlı değilse ısıtma hiç kurulmaz; zamanlayıcı bile açılmaz.
1141
+ - Bu hook tanımlı değilse klasik ısıtma hiç kurulmaz; zamanlayıcı bile açılmaz.
1142
+ (`onVisit` modunda hook **yasaktır**, yukarıya bakın.)
1093
1143
 
1094
- ### Tur mantığı
1144
+ ### Tur mantığı (klasik)
1095
1145
 
1096
1146
  1. Liste toplanır. `max`'tan (varsayılan 400) uzunsa bir dilim seçilir:
1097
1147
  `priority` eşleşenler **her turda** başa alınır, kalan yerler kuyruktan
@@ -1272,8 +1322,12 @@ turunun gerçekten `MISS` → önbellek doldurup doldurmadığını buradan gör
1272
1322
  fazla `revalidate` + bir tazeleme turudur.
1273
1323
  - **Önbellek şişiyor.** Query parametreleri anahtara girdiği için kampanya
1274
1324
  parametreleri girdi çoğaltıyor olabilir.
1275
- - **Isıtma hiç çalışmıyor.** `hooks.prewarmPaths` tanımlı değil, `PREWARM=0`
1276
- ayarlı ya da `cache().prewarm.enabled === false`.
1325
+ - **Isıtma hiç çalışmıyor.** Klasik modda `hooks.prewarmPaths` tanımlı değil,
1326
+ `PREWARM=0` ayarlı ya da `cache().prewarm.enabled === false`. `onVisit`
1327
+ modunda `listen` sonrası logda `onVisit mode` satırını ve public cache'li
1328
+ bir sayfa gezildiğini doğrulayın.
1329
+ - **Config `onVisit` + `max` / `prewarmPaths` ile düşüyor.** İki mod karşılıklı
1330
+ dışlayıcı; yalnızca birini kullanın.
1277
1331
  - **Isıtma turu API'yi 429'a sokuyor.** `rps` verilmemiş. `concurrency`
1278
1332
  düşürmek yeterli değil; kotayı koruyan ayar toplam hız. Kalıcı çözüm veri
1279
1333
  önbelleği: ikinci turdan sonra ısıtma upstream'e gitmez.
@@ -882,6 +882,11 @@ tam cevabı olmadığı — [06-cache.md](./06-cache.md) içinde.
882
882
 
883
883
  ### `cache().prewarm`
884
884
 
885
+ İki mod: **klasik** (liste + açılış turu) veya **`onVisit`** (ziyaret edilen
886
+ sayfadaki linkler). Birlikte verilemez — config yüklenirken hata.
887
+
888
+ #### Klasik alanlar
889
+
885
890
  | Alan | Tip | Varsayılan | Anlamı |
886
891
  | --- | --- | --- | --- |
887
892
  | `enabled` | `boolean` | `true` | `false` ise ısıtma yapılmaz (`PREWARM=1` ile ezilebilir) |
@@ -910,6 +915,24 @@ prewarm: {
910
915
  }
911
916
  ```
912
917
 
918
+ #### `onVisit`
919
+
920
+ | Alan | Tip | Varsayılan | Anlamı |
921
+ | --- | --- | --- | --- |
922
+ | `onVisit` | `true \| false \| object` | kapalı | Ziyaret tabanlı ısıtma |
923
+ | `onVisit.perPage` | `number` | `20` | Sayfa başına üstten alta en fazla link |
924
+ | `onVisit.concurrency` | `number` | klasik ile aynı | Paralel işçi |
925
+ | `onVisit.rps` | `number` | klasik ile aynı | Saniyedeki tavan; `0` sınırsız |
926
+
927
+ ```js
928
+ prewarm: {
929
+ onVisit: { perPage: 20, rps: 4 },
930
+ }
931
+ ```
932
+
933
+ `hooks.prewarmPaths` ve klasik alanlar (`max`, `priority`, …) `onVisit` ile
934
+ **yasaktır**. Ayrıntı: [06-cache.md](./06-cache.md).
935
+
913
936
  Sayısal alanların her biri aynı adı taşıyan ortam değişkeniyle ezilebilir; env
914
937
  önceliklidir. Ayrıntı: [06-cache.md](./06-cache.md).
915
938
 
@@ -926,7 +949,7 @@ varsayılanına döner ve uyarır — sayfa düşmez.
926
949
  | `layoutContext` | `({ pathname, metadata }) => object` | Layout local'leri; `lang`, `structuredData`, `extraHead`, `bodyClass` özel yorumlanır | [04](./04-render-ve-sablonlar.md) |
927
950
  | `notFound` | `() => object \| null` | 404 sayfa tanımı; `null` ise framework'ün hata sayfası | [03](./03-routing.md) |
928
951
  | `error` | `({ status, error }) => object \| string \| null` | 404 dışındaki hata sayfaları (ve `notFound` yoksa 404); sayfa tanımı ya da doğrudan HTML | [03](./03-routing.md) |
929
- | `prewarmPaths` | `() => string[]` | Isıtılacak yollar; tanımlı değilse ısıtma hiç kurulmaz | [06](./06-cache.md) |
952
+ | `prewarmPaths` | `() => string[]` | Klasik ısıtmada ısıtılacak yollar; tanımlı değilse klasik tur kurulmaz. `onVisit` ile birlikte **yasak** | [06](./06-cache.md) |
930
953
 
931
954
  ```js
932
955
  hooks: {
@@ -177,9 +177,13 @@ Tüm arayüz shadow DOM içinde durur, sayfanın CSS'i ile karışmaz.
177
177
  Gösterdikleri:
178
178
 
179
179
  - **Hatalar:** tarayıcı tarafındaki JS hataları, kaynak yükleme hataları
180
- (`img`/`script`/`link`), ve sunucudaki `console.error` / `console.warn`
181
- çıktıları. Sunucu tarafında `console` sarılır, böylece uyarılar terminalde
182
- kaybolmaz.
180
+ (`img`/`script`/`link`), sunucudaki `console.error` / `console.warn`
181
+ çıktıları, ve SSR / tarayıcı `fetch` çağrılarının 4xx/5xx ya da ağ
182
+ başarısızlıkları. Her kayıt mümkün olduğunca **sayfa yolu**, **API URL** ve
183
+ (istemcide) **island adı** taşır; yanıt gövdesi **show details** ile açılır —
184
+ `[object Object]` yerine JSON. Sunucu tarafında `console` sarılır, böylece
185
+ uyarılar terminalde kaybolmaz; upstream hataları uygulamanın kendi logger'ı
186
+ stderr'e yazsa bile overlay'e düşer.
183
187
  - **SEO:** açık sayfanın istemci tarafı taraması — title ve meta description
184
188
  uzunluğu, `html lang`, viewport, canonical, robots/`noindex`, Open Graph ve
185
189
  Twitter etiketleri, H1/outline, görsel `alt`, boş linkler ve JSON-LD parse
@@ -244,9 +248,11 @@ http://localhost:3000/__jskelet/dev/report
244
248
  gezilmemiş ama ısıtılmış sayfalar da listelenir: SSR tarafı bilinir, istemci
245
249
  ölçümleri boş kalır.
246
250
  - **Sunucu API çağrıları:** SSR sırasında yapılan dış `fetch` çağrıları — URL,
247
- host, metot, durum, süre, bayt. `globalThis.fetch` yalnızca development'ta
251
+ host, metot, durum, süre, bayt, hangi sayfa render edilirken yapıldığı ve
252
+ başarısız cevaplarda gövde özeti. `globalThis.fetch` yalnızca development'ta
248
253
  sarılır; üretim yolu dokunulmaz kalır. Kendi sunucumuza yapılan istekler
249
- (ısıtma, sağlık kontrolü) API sayılmaz.
254
+ (ısıtma, sağlık kontrolü) API sayılmaz. Başarısız çağrılar overlay Errors
255
+ sekmesine de düşer.
250
256
  - **Build çıktısı:** manifest'teki her varlığın ham/gzip/brotli boyutu, ve
251
257
  esbuild metafile'ından chunk analizi — her çıktının boyutu, hangi kaynaklardan
252
258
  oluştuğu, hangi chunk'ları import ettiği. Kaynaklar okunur gruplara indirgenir
@@ -1051,20 +1051,69 @@ before anything else: `Cache-Control: private`, `Set-Cookie` and query string
1051
1051
  settings are the most common reasons an edge decides not to cache, and they
1052
1052
  show up as `dynamic` in this panel.
1053
1053
 
1054
- ## Prewarm — warming up at startup
1054
+ ## Prewarm — warming at startup or on visit
1055
1055
 
1056
1056
  The equivalent of Next's build-time prerender, except the output is not written
1057
- to disk: since the cache lives in process memory, the warm-up also happens when
1058
- the process comes up. The gain is the same — the first visitor does not wait
1059
- for a cold render — but the data is not frozen; every entry ages with the
1060
- route's `revalidate` and is refreshed in the background with
1061
- stale-while-revalidate.
1057
+ to disk: since the cache lives in process memory, the warm-up happens when the
1058
+ process comes up (classic mode) or as traffic arrives (`onVisit`). The gain is
1059
+ the same — the clicked / neighbouring page does not wait for a cold render —
1060
+ but the data is not frozen; every entry ages with the route's `revalidate` and
1061
+ is refreshed in the background with stale-while-revalidate.
1062
1062
 
1063
1063
  The warm-up is done with **real HTTP requests**
1064
1064
  (`http://127.0.0.1:<port>`), so that the cache key, the compression and the
1065
1065
  middleware chain are exactly the same as with normal traffic.
1066
1066
 
1067
- ### `hooks.prewarmPaths()`
1067
+ The two modes are **mutually exclusive**. If `cache().prewarm.onVisit` is on,
1068
+ classic fields (`max`, `priority`, `rotate`, `intervalSeconds`, …) and
1069
+ `hooks.prewarmPaths` must not be set together — config load throws. The reverse
1070
+ holds too: if you use classic list warming, do not set `onVisit`.
1071
+
1072
+ ### Mode: `onVisit` — links from the page just visited
1073
+
1074
+ When a user receives a public, cacheable page (`public` HTML, 200), the
1075
+ framework takes same-origin `<a href>` paths from the response HTML (top to
1076
+ bottom, up to `perPage`) and warms them in the background. The next click, or
1077
+ another visitor to the same neighbourhood, usually sees a `HIT`.
1078
+
1079
+ ```js
1080
+ // jskelet.config.mjs
1081
+ export default {
1082
+ async cache() {
1083
+ return {
1084
+ html: { "/": 60, "/news/:slug": 300 },
1085
+ prewarm: {
1086
+ onVisit: {
1087
+ perPage: 20, // at most this many links per page
1088
+ concurrency: 2, // optional
1089
+ rps: 4, // optional; 0 = unlimited
1090
+ },
1091
+ },
1092
+ };
1093
+ },
1094
+ };
1095
+ ```
1096
+
1097
+ `onVisit: true` is enough (default `perPage: 20`).
1098
+
1099
+ Rules:
1100
+
1101
+ - Only **public + cacheable** 200 HTML from `route()` triggers it; `private`,
1102
+ degraded or `no-store` responses do not extract links.
1103
+ - The warmer's own UA (`brand.prewarmUserAgent`) does not trigger — no crawl
1104
+ loop.
1105
+ - Paths that are already fresh are not enqueued.
1106
+ - `nofollow`, `target="_blank"`, `data-no-prefetch`, `prewarmSkip` and
1107
+ `navigation.exclude` share the same exemptions as Speculation Rules.
1108
+ - Query strings are not warmed (default cache policy treats query as dynamic).
1109
+ - There is no automatic startup pass; the first visitor to a page may still pay
1110
+ a MISS. If you need critical paths hot before traffic, prefer classic mode or
1111
+ readiness + a seed.
1112
+ - `PREWARM=0` turns onVisit off too. `PREWARM_MAX` / `PREWARM_INTERVAL_SECONDS`
1113
+ / `PREWARM_DELAY_MS` / `PREWARM_RETRY_DELAY_MS` cannot be used with onVisit
1114
+ (error).
1115
+
1116
+ ### Mode: classic — `hooks.prewarmPaths()`
1068
1117
 
1069
1118
  The application declares which paths get warmed; usually it is the very same
1070
1119
  function that produces the sitemap.
@@ -1090,11 +1139,10 @@ Rules:
1090
1139
  not be warmed.
1091
1140
  - Deduplication **preserves order**: when no `priority` is given, the order the
1092
1141
  application provides is meaningful — put the most important pages first.
1093
- - If this hook is not defined the warm-up is never set up; not even the timer
1094
- is started.
1095
-
1096
- ### Round logic
1142
+ - If this hook is not defined the classic warm-up is never set up; not even the
1143
+ timer is started. (In `onVisit` mode the hook is **forbidden** — see above.)
1097
1144
 
1145
+ ### Round logic (classic)
1098
1146
  1. The list is collected. If it is longer than `max` (400 by default) a slice is
1099
1147
  selected: the paths matching `priority` are taken first **on every round**,
1100
1148
  and the remaining slots are filled from the queue.
@@ -1277,8 +1325,12 @@ filled the cache.
1277
1325
  lag is at most `revalidate` + one refresh round.
1278
1326
  - **The cache is bloating.** Because query parameters go into the key, campaign
1279
1327
  parameters may be multiplying entries.
1280
- - **The warm-up never runs.** `hooks.prewarmPaths` is not defined, `PREWARM=0`
1281
- is set, or `cache().prewarm.enabled === false`.
1328
+ - **The warm-up never runs.** In classic mode `hooks.prewarmPaths` is not
1329
+ defined, `PREWARM=0` is set, or `cache().prewarm.enabled === false`. In
1330
+ `onVisit` mode check the `onVisit mode` log line after `listen` and that a
1331
+ public cacheable page was visited.
1332
+ - **Config fails with `onVisit` + `max` / `prewarmPaths`.** The two modes are
1333
+ mutually exclusive; use only one.
1282
1334
  - **The warm-up round pushes the API into 429.** No `rps` was given. Lowering
1283
1335
  `concurrency` is not enough; the setting that protects the quota is the total
1284
1336
  rate. The lasting fix is the data cache: after the second round the warm-up
@@ -902,6 +902,11 @@ edges hold this page" has no exact answer — is in
902
902
 
903
903
  ### `cache().prewarm`
904
904
 
905
+ Two modes: **classic** (list + startup pass) or **`onVisit`** (links from the
906
+ page just visited). They cannot be combined — config load throws.
907
+
908
+ #### Classic fields
909
+
905
910
  | Field | Type | Default | Meaning |
906
911
  | --- | --- | --- | --- |
907
912
  | `enabled` | `boolean` | `true` | If `false`, no prewarming happens (can be overridden with `PREWARM=1`) |
@@ -930,6 +935,24 @@ prewarm: {
930
935
  }
931
936
  ```
932
937
 
938
+ #### `onVisit`
939
+
940
+ | Field | Type | Default | Meaning |
941
+ | --- | --- | --- | --- |
942
+ | `onVisit` | `true \| false \| object` | off | Visit-driven warming |
943
+ | `onVisit.perPage` | `number` | `20` | At most how many links per page (top to bottom) |
944
+ | `onVisit.concurrency` | `number` | same as classic | Parallel workers |
945
+ | `onVisit.rps` | `number` | same as classic | Requests per second cap; `0` unlimited |
946
+
947
+ ```js
948
+ prewarm: {
949
+ onVisit: { perPage: 20, rps: 4 },
950
+ }
951
+ ```
952
+
953
+ `hooks.prewarmPaths` and classic fields (`max`, `priority`, …) are **forbidden**
954
+ with `onVisit`. Details: [06-caching.md](./06-caching.md).
955
+
933
956
  Each numeric field can be overridden by an environment variable of the same
934
957
  name; env takes precedence. Details: [06-caching.md](./06-caching.md).
935
958
 
@@ -946,7 +969,7 @@ its own default and warns — the page does not go down.
946
969
  | `layoutContext` | `({ pathname, metadata }) => object` | Layout locals; `lang`, `structuredData`, `extraHead` and `bodyClass` get special treatment | [04](./04-rendering.md) |
947
970
  | `notFound` | `() => object \| null` | 404 page definition; if `null`, the framework's error page | [03](./03-routing.md) |
948
971
  | `error` | `({ status, error }) => object \| string \| null` | Error pages other than 404 (and 404 when there is no `notFound`); a page definition or HTML directly | [03](./03-routing.md) |
949
- | `prewarmPaths` | `() => string[]` | Paths to prewarm; if it is not defined, prewarming is never set up | [06](./06-caching.md) |
972
+ | `prewarmPaths` | `() => string[]` | Paths for classic prewarm; if omitted, the classic pass is never set up. **Forbidden** with `onVisit` | [06](./06-caching.md) |
950
973
 
951
974
  ```js
952
975
  hooks: {
@@ -181,9 +181,13 @@ the page's CSS.
181
181
  What it shows:
182
182
 
183
183
  - **Errors:** browser-side JS errors, resource loading errors
184
- (`img`/`script`/`link`), and the server's `console.error` / `console.warn`
185
- output. On the server side `console` is wrapped so warnings do not get lost in
186
- the terminal.
184
+ (`img`/`script`/`link`), the server's `console.error` / `console.warn`
185
+ output, and failed SSR / browser `fetch` calls (4xx/5xx or network). Each
186
+ record carries a **page path**, **API URL**, and (on the client) an **island
187
+ name** when known; the response body opens under **show details** as JSON
188
+ instead of `[object Object]`. On the server side `console` is wrapped so
189
+ warnings do not get lost in the terminal; upstream failures still land in the
190
+ overlay even when the app's own logger writes them only to stderr.
187
191
  - **SEO:** a client-side scan of the current page — title and meta description
188
192
  length, `html lang`, viewport, canonical, robots/`noindex`, Open Graph and
189
193
  Twitter tags, H1/outline, image `alt`, empty links, and JSON-LD parse errors.
@@ -248,9 +252,10 @@ Its contents:
248
252
  status of the SSR output. Pages that were never visited but were warmed are
249
253
  listed too: the SSR side is known, the client measurements stay empty.
250
254
  - **Server API calls:** outbound `fetch` calls made during SSR — URL, host,
251
- method, status, duration, bytes. `globalThis.fetch` is only wrapped in
252
- development; the production path is left untouched. Requests to our own server
253
- (warming, health check) do not count as API calls.
255
+ method, status, duration, bytes, which page was rendering, and a body summary
256
+ on failures. `globalThis.fetch` is only wrapped in development; the production
257
+ path is left untouched. Requests to our own server (warming, health check) do
258
+ not count as API calls. Failures also appear on the overlay Errors tab.
254
259
  - **Build output:** the raw/gzip/brotli size of every asset in the manifest, and
255
260
  chunk analysis from esbuild's metafile — the size of each output, which
256
261
  sources it is made of, which chunks it imports. Sources are reduced to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jskelet",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "A framework that feels like no framework: Express 5 + build-time .jsk (or EJS) SSR, vanilla JS islands, Tailwind v4 and an in-process HTML TTL cache.",
5
5
  "type": "module",
6
6
  "license": "MIT",