jskelet 0.6.0 → 0.6.2

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/AGENTS.md CHANGED
@@ -29,8 +29,7 @@ zaten tartışılmış:
29
29
 
30
30
  Belgeler iki dilde: Türkçesi `docs/`, İngilizcesi `docs/en/` altında ve
31
31
  dosyalar birebir eşlenik. Bir belgeyi değiştirdiysen karşılığını da güncelle;
32
- pazarlama sitesi (`examples/marketing`) bu dosyaları doğrudan `/docs` altında
33
- servis ettiği için eksik kalan çeviri kullanıcıya görünür.
32
+ eksik kalan çeviri kullanıcıya görünür.
34
33
 
35
34
  ## Doğrulama
36
35
 
@@ -123,7 +122,7 @@ sessizce çıktıdan düşer.
123
122
 
124
123
  Framework'ün genel yüzeyini değiştirdiysen (`route()` imzası, hook adları,
125
124
  config alanları, client API'si) `examples/minimal`, `examples/blog` ve
126
- `examples/marketing`'i de güncelle. Örnekler belgelerdeki kod parçalarının kaynağı; kaymaları en hızlı
125
+ `examples/dashboard`'i de güncelle. Örnekler belgelerdeki kod parçalarının kaynağı; kaymaları en hızlı
127
126
  fark edilen yer orası.
128
127
 
129
128
  ## Windows notları
package/CHANGELOG.md CHANGED
@@ -10,121 +10,175 @@ one is listed under a **Breaking** heading.
10
10
 
11
11
  ### Added
12
12
 
13
- - `jskelet dev --murder` / `jskelet start --murder`: if the listen port is
14
- already taken, kill the listener and bind; without `--murder`, refuse to
15
- start with a clear error (pid + hint) instead of a bare `EADDRINUSE`.
16
- - Layout built-ins for `.jsk`: `Stylesheets`, `BodyScripts`, and `JsonLd`
17
- (asset / script / JSON-LD loops without expression-language calls).
18
- - Framework default layout as `src/templates/layout.jsk` with a checked-in
19
- `layout.render.js` (`node scripts/compile-framework-layout.mjs`;
20
- `--check` for drift). `jskelet/layout` points at the `.jsk` source;
21
- legacy EJS copy remains at `jskelet/layout/ejs`.
22
- - VS Code / Cursor JSK extension diagnostics (Problems on open/save) and
23
- PascalCase component completions from `views/components` (v0.2.0).
24
- - `<!DOCTYPE>` (and other `<!…>` declarations) parse correctly in `.jsk`
25
- instead of hanging the compiler.
26
-
27
- - `jskelet migrate` codemod for Next.js App Router → JSkelet: `scan` inventory,
28
- `apply` (JSX pages → controller + `.jsk`, presentational components → HTML
29
- string helpers, `"use client"` → island stubs), and `config` draft from
30
- `next.config`. Ships with `@babel/parser` / `@babel/types`.
31
- - Published TypeScript declaration files under `types/` for `jskelet`,
32
- `jskelet/client`, `jskelet/html`, `jskelet/tags`, `jskelet/cookies` and
33
- `jskelet/log` (`npm run types`).
34
- - Client build accepts `.ts` / `.mts` entries and islands (esbuild); manifest
35
- keys stay `*.js`. Conflicting stems (`main.js` + `main.ts`) fail the build.
36
- Icon usage scan includes `.ts` / `.mts`.
37
-
38
- - Local flat `icons/` directory as the exclusive SVG sprite source when present
39
- (`icons.dir`, default `"icons"`): `house.svg` / `house-bold.svg` file names,
40
- XOR with `@phosphor-icons/core` (Phosphor only when the directory is absent).
41
- The hashed sprite still lands under `public/assets/` and is precompressed.
42
- - Auth handoff hardening: `allowedCookieNames` allowlist, pending-ticket and
43
- per-IP mint limits, and RFC 6265 cookie-name validation on `serializeCookie`
44
- (`isValidCookieName`).
13
+ - `robots.txt` responses gain a trailing JSkelet note that disallows framework
14
+ endpoints (`/_jskelet/`, `/__jskelet/`, `/_fragment/`, plus a custom admin,
15
+ image, handoff, or dev path when it sits outside those prefixes). Every
16
+ user-agent already named in the file is repeated in that block, so a
17
+ crawler-specific group still sees the rules.
18
+
19
+ ### Breaking
20
+
21
+ - Dev gate is opt-in
22
+ `DEV_TOKEN` in the environment no longer locks the site. Require the token
23
+ only with `devGate: true` or `DEV_GATE=1`. `DEV_GATE=0` turns the gate off
24
+ even when config enabled it.
25
+ - Cache ceilings
26
+ `cache().maxEntries` above 800, `cache().data.maxEntries` above 20,000, and
27
+ `prewarm.onVisit` above `perPage` 20, `rps` 2, or `concurrency` 2 are clamped
28
+ at load with a warning. `onVisit` `rps: 0` is no longer unlimited. The HTML
29
+ cache also stops growing past 256 MB of stored HTML plus compressed bodies;
30
+ a single page larger than that is not stored.
45
31
 
46
32
  ### Fixed
47
33
 
48
- - `jskelet dev` prints the real server failure (e.g. port already in use /
34
+ - Visit warming skips pages that are already fresh when the cache key has a
35
+ vary prefix or a trailing `?`. With `vary.host`, warm requests stay on
36
+ loopback and send the public host as `x-forwarded-host`, so a second
37
+ `127.0.0.1` HTML entry is not created. The onVisit queue holds at most 64
38
+ paths.
39
+
40
+ ### Removed
41
+
42
+ - `examples/marketing` — the marketing site now lives as a standalone app
43
+ outside this repository (`jskelet-marketing`).
44
+
45
+ ## [0.6.0] - 2026-09-21
46
+
47
+ ### Added
48
+
49
+ - Port reclaim with `--murder`
50
+ `jskelet dev --murder` and `jskelet start --murder` kill whatever already
51
+ holds the listen port and bind in its place. Without the flag the CLI still
52
+ refuses to start, but now with a clear error (pid + hint) instead of a bare
53
+ `EADDRINUSE`.
54
+ - Layout built-ins for `.jsk`
55
+ `<Stylesheets />`, `<BodyScripts />`, and `<JsonLd />` emit the asset /
56
+ script / JSON-LD loops from layout without calling helpers in the expression
57
+ language.
58
+ - Framework default layout as `.jsk`
59
+ Ships as `src/templates/layout.jsk` with a checked-in `layout.render.js`
60
+ (`node scripts/compile-framework-layout.mjs`, `--check` for drift).
61
+ `jskelet/layout` points at the `.jsk` source; the legacy EJS copy remains at
62
+ `jskelet/layout/ejs`.
63
+ - JSK editor tooling
64
+ The VS Code / Cursor JSK extension (v0.2.0) reports diagnostics in Problems
65
+ on open/save and offers PascalCase component completions from
66
+ `views/components`.
67
+ - `<!DOCTYPE>` parsing in `.jsk`
68
+ Doctype and other `<!…>` declarations parse correctly instead of hanging the
69
+ template compiler.
70
+ - `jskelet migrate` for Next.js App Router
71
+ Codemod with `scan` inventory, `apply` (JSX pages → controller + `.jsk`,
72
+ presentational components → HTML string helpers, `"use client"` → island
73
+ stubs), and `config` draft from `next.config`. Ships with `@babel/parser` /
74
+ `@babel/types`.
75
+ - Published TypeScript declarations
76
+ `.d.ts` files under `types/` for `jskelet`, `jskelet/client`, `jskelet/html`,
77
+ `jskelet/tags`, `jskelet/cookies`, and `jskelet/log` (`npm run types`).
78
+ - TypeScript client entries and islands
79
+ The client build accepts `.ts` / `.mts` entries and islands via esbuild;
80
+ manifest keys stay `*.js`. Conflicting stems (`main.js` + `main.ts`) fail the
81
+ build. Icon usage scan includes `.ts` / `.mts`.
82
+ - Local `icons/` sprite source
83
+ When a flat `icons/` directory is present (`icons.dir`, default `"icons"`),
84
+ it is the exclusive SVG sprite source (`house.svg` / `house-bold.svg`).
85
+ Phosphor is used only when that directory is absent. The hashed sprite still
86
+ lands under `public/assets/` and is precompressed.
87
+ - Auth handoff hardening
88
+ `allowedCookieNames` allowlist, pending-ticket and per-IP mint limits, plus
89
+ RFC 6265 cookie-name validation on `serializeCookie` (`isValidCookieName`).
90
+
91
+ ### Fixed
92
+
93
+ - Clearer `jskelet dev` startup failures
94
+ The CLI prints the real server failure (for example port already in use /
49
95
  `--murder` hint) instead of only `server exited (code 1)` when the child
50
96
  dies during startup.
51
- - Marketing example icon sprite again includes trust-bar icons (`Package`,
52
- `TextT`, `ShieldCheck`, …): `icons.scan` now covers `routes/` where those
53
- names live after the `.jsk` migration (they were only declared in
54
- controllers, not in scanned `lib`/`views`).
55
-
56
- - Open Graph routes with a `.png` suffix (`/og/…/:slug.png`) now escape the
57
- dot for Express 5 / path-to-regexp, so the handler matches again instead of
58
- falling through to the HTML 404.
59
- - Remote image optimizer no longer auto-follows redirects; each hop is
60
- re-checked against `allowHosts`, blocked private addresses, and DNS
61
- resolution (open-redirect SSRF).
97
+ - Marketing trust-bar icons after `.jsk` migration
98
+ `icons.scan` now covers `routes/`, so names declared in controllers
99
+ (`Package`, `TextT`, `ShieldCheck`, …) land in the sprite again.
100
+ - Open Graph routes with a `.png` suffix
101
+ Paths like `/og/…/:slug.png` escape the dot for Express 5 / path-to-regexp,
102
+ so the handler matches instead of falling through to the HTML 404.
103
+ - Safer remote image optimizer redirects
104
+ Redirects are no longer followed blindly; each hop is re-checked against
105
+ `allowHosts`, blocked private addresses, and DNS resolution (open-redirect
106
+ SSRF).
62
107
 
63
108
  ### Breaking
64
109
 
65
- - `ejs` is an optional peer dependency. Apps that only use `.jsk` need not
66
- install it; apps that still have `.ejs` views or layouts must
67
- `npm i ejs`. Missing EJS when an `.ejs` file is rendered throws a clear
68
- install/migrate hint.
69
- - `jskelet/layout` now resolves to `layout.jsk` (was `layout.ejs`). Use
110
+ - `ejs` is an optional peer
111
+ Apps that only use `.jsk` need not install it; apps that still have `.ejs`
112
+ views or layouts must `npm i ejs`. Missing EJS when an `.ejs` file is
113
+ rendered throws a clear install/migrate hint.
114
+ - Default layout export is `.jsk`
115
+ `jskelet/layout` now resolves to `layout.jsk` (was `layout.ejs`). Use
70
116
  `jskelet/layout/ejs` for the legacy file.
71
-
72
- - `auth.crossSubdomainHandoff` mint requires a non-empty
73
- `allowedCookieNames` list; `true` alone no longer accepts arbitrary cookie
74
- names.
75
- - Production client builds omit sourcemaps (development still emits them).
76
- - Secret-like `clientEnv` key names fail the build instead of being inlined.
117
+ - Auth handoff requires cookie allowlist
118
+ `auth.crossSubdomainHandoff` mint needs a non-empty `allowedCookieNames`
119
+ list; `true` alone no longer accepts arbitrary cookie names.
120
+ - Production builds omit client sourcemaps
121
+ Development still emits them; production client bundles do not.
122
+ - Secret-like `clientEnv` keys fail the build
123
+ Names that look like secrets are rejected instead of being inlined into the
124
+ client bundle.
77
125
 
78
126
  ### Changed
79
127
 
80
- - Examples (`minimal`, `blog`, `dashboard`, `marketing`) use `.jsk` only
81
- (layouts, pages, partials); EJS files removed from those trees.
82
- - Template compiler: unknown PascalCase components fail the build; clearer
83
- errors for forbidden function calls, unknown includes (with location), and
84
- unclosed `{#if}` / `{#each}` at EOF; icon scan recognizes `<Icon name="…" />`.
85
- - Docs / AGENTS / README present build-time `.jsk` as the default story; EJS
86
- is documented as an optional legacy peer.
87
-
88
- - VS Code / Cursor extension (`extensions/vscode-jsk`) uses the new 3D `.jsk`
89
- mark as both the marketplace extension icon and the explorer file icon for
90
- `*.jsk`. Packages as a standalone VSIX (vendors `src/compile`) for
91
- Marketplace publish.
92
- - `@babel/parser` and `@babel/types` are optional peers used only by migrate
93
- (JSX/TSX parsing). They are no longer installed with every `jskelet`
94
- install; missing peers throw an install hint (`npm i -D @babel/parser
95
- @babel/types`). Unused `@babel/traverse` was dropped.
96
-
97
- - Auth handoff mint mounts **after** CSRF so origin checks apply to
128
+ - Marketing changelog as release notes
129
+ `/changelog` hides Unreleased, lists every published release without
130
+ pagination, and shows each note as a titled card with a short headline plus
131
+ a longer explanation (Keep-a-Changelog sections: Added / Changed / Fixed /
132
+ …).
133
+ - Examples are `.jsk` only
134
+ `minimal`, `blog`, `dashboard`, and `marketing` use `.jsk` for layouts,
135
+ pages, and partials; EJS files were removed from those trees.
136
+ - Stricter, clearer template compiler
137
+ Unknown PascalCase components fail the build. Errors for forbidden function
138
+ calls, unknown includes (with location), and unclosed `{#if}` / `{#each}` at
139
+ EOF are clearer; icon scan recognizes `<Icon name="…" />`.
140
+ - Docs present `.jsk` as the default
141
+ Docs / AGENTS / README tell the build-time `.jsk` story first; EJS is
142
+ documented as an optional legacy peer.
143
+ - Standalone JSK VSIX packaging
144
+ The VS Code / Cursor extension (`extensions/vscode-jsk`) uses the new 3D
145
+ `.jsk` mark as marketplace and explorer icon, and packages as a standalone
146
+ VSIX (vendors `src/compile`) for Marketplace publish.
147
+ - Migrate peers are optional
148
+ `@babel/parser` and `@babel/types` are optional peers used only by migrate.
149
+ They are no longer installed with every `jskelet` install; missing peers
150
+ throw an install hint. Unused `@babel/traverse` was dropped.
151
+ - Auth handoff sits after CSRF
152
+ Mint mounts after CSRF so origin checks apply to
98
153
  `POST /_jskelet/auth/handoff`.
99
- - Docs (TR/EN): stronger `trustProxy` / `csrf.token` guidance and a fuller
100
- security-headers example under `headers()`.
101
- - Marketing copy (EN/TR) and how-it-works examples present `.jsk` as the
102
- default template surface instead of EJS; the compare column for hand-written
103
- Express + EJS stays as a competitor. Scaffold and migrate mappings name
104
- `.jsk` pages and layouts.
105
- - Framework and marketing brand mark: new geometric logo at `src/logo.png`
106
- (admin / devtools) and `examples/marketing/public/logo.png`, replacing the
107
- CDN-hosted mark. Marketing serves local favicons (`favicon.ico`, 16/32 PNG,
108
- apple-touch-icon) via metadata `extraTags`.
109
- - In development (`NODE_ENV=development`), 5xx responses show a diagnostic
110
- page with the error message and stack trace instead of the polished 500
111
- status page / `hooks.error()`. Production still returns the minimal status
112
- page with no internals.
113
- - Marketing fit copy (EN/TR) treats signed-in dashboards and per-visitor
114
- panels as a supported path (`private: true`, fragments) instead of a
115
- “wrong choice”; the poor-fit column now names SPA shells, collaborative
116
- client trees, streaming/RSC, and built-in real-time transport.
117
- - Marketing example copy (EN/TR) reflects 0.5.x cache surfaces: host `vary`,
118
- early refresh, classic vs `onVisit` prewarm, local `icons/`, shared cookies,
119
- and `opengraph-image` → `ogHandler` on the migrate table. Pages now serve
120
- per-locale dynamic OG cards at `/og/:locale/:page.png`.
121
- - Marketing changelog page restyled like a release-notes browser: measured
122
- summary cards, search and newest/oldest sort, paginated open release cards
123
- with Latest / Released badges and GitHub links (still driven by `CHANGELOG.md`).
124
- - Marketing example visual language tightened toward the dark cyan glass look:
125
- shared `glass-panel` surfaces site-wide, numbered lit feature cards, a
126
- 3D stack illustration on the home hero, and rotating cyan border beams on
127
- lit panels.
154
+ - Stronger security docs
155
+ Docs (TR/EN) expand `trustProxy` / `csrf.token` guidance and include a
156
+ fuller security-headers example under `headers()`.
157
+ - Marketing copy defaults to `.jsk`
158
+ EN/TR marketing and how-it-works examples present `.jsk` as the default
159
+ template surface; the compare column for hand-written Express + EJS stays as
160
+ a competitor. Scaffold and migrate mappings name `.jsk` pages and layouts.
161
+ - New geometric brand mark
162
+ Framework and marketing logos live at `src/logo.png` (admin / devtools) and
163
+ `examples/marketing/public/logo.png`. Marketing serves local favicons via
164
+ metadata `extraTags`.
165
+ - Development 5xx diagnostics
166
+ In `NODE_ENV=development`, 5xx responses show the error message and stack
167
+ instead of the polished 500 page / `hooks.error()`. Production still returns
168
+ the minimal status page with no internals.
169
+ - Marketing fit copy for signed-in panels
170
+ EN/TR fit copy treats dashboards and per-visitor panels as a supported path
171
+ (`private: true`, fragments). The poor-fit column names SPA shells,
172
+ collaborative client trees, streaming/RSC, and built-in real-time transport.
173
+ - Marketing copy for 0.5.x surfaces
174
+ EN/TR copy reflects host `vary`, early refresh, classic vs `onVisit`
175
+ prewarm, local `icons/`, shared cookies, and `opengraph-image` → `ogHandler`
176
+ on the migrate table. Pages serve per-locale OG cards at
177
+ `/og/:locale/:page.png`.
178
+ - Marketing visual language
179
+ Dark cyan glass look with shared `glass-panel` surfaces, numbered lit feature
180
+ cards, a 3D stack illustration on the home hero, and rotating cyan border
181
+ beams on lit panels.
128
182
 
129
183
  ## [0.5.4] - 2026-09-18
130
184
 
@@ -503,7 +557,8 @@ Initial release.
503
557
  - Documentation under `docs/` and three examples: `minimal`, `blog`,
504
558
  `marketing`.
505
559
 
506
- [Unreleased]: https://github.com/ayberkenis/jskelet/compare/v0.5.4...HEAD
560
+ [Unreleased]: https://github.com/ayberkenis/jskelet/compare/v0.6.0...HEAD
561
+ [0.6.0]: https://github.com/ayberkenis/jskelet/compare/v0.5.4...v0.6.0
507
562
  [0.5.4]: https://github.com/ayberkenis/jskelet/compare/v0.5.3...v0.5.4
508
563
  [0.5.3]: https://github.com/ayberkenis/jskelet/compare/v0.5.2...v0.5.3
509
564
  [0.5.2]: https://github.com/ayberkenis/jskelet/compare/v0.5.1...v0.5.2
package/README.md CHANGED
@@ -307,12 +307,13 @@ Anything reachable by a deeper path is internal and may change without notice.
307
307
  ## Deployment
308
308
 
309
309
  The server is a plain Express 5 app, so anything that can run a Node process
310
- works: a `Dockerfile` (see `examples/marketing/Dockerfile`), a systemd unit, or
311
- a PaaS. Run `jskelet build` at image build time, put a reverse proxy in front
312
- for TLS, and expose a health endpoint (the default dev gate bypass list already
313
- includes `/api/healthcheck`, so a route there is reachable in every mode).
314
- Details, including cache sizing behind multiple instances and the optional
315
- Redis tier, are in [docs/10-dagitim.md](./docs/10-dagitim.md) /
310
+ works: a multi-stage `Dockerfile` (see [docs/10-dagitim.md](./docs/10-dagitim.md)),
311
+ a systemd unit, or a PaaS. Run `jskelet build` at image build time, put a
312
+ reverse proxy in front for TLS, and expose a health endpoint (the default
313
+ dev gate bypass list already includes `/api/healthcheck`, so a route there is
314
+ reachable in every mode). Details, including cache sizing behind multiple
315
+ instances and the optional Redis tier, are in
316
+ [docs/10-dagitim.md](./docs/10-dagitim.md) /
316
317
  [docs/en/10-deployment.md](./docs/en/10-deployment.md).
317
318
 
318
319
  ## Documentation
@@ -343,7 +344,6 @@ apply to this repository.
343
344
  ```bash
344
345
  npm --prefix examples/minimal install && npm --prefix examples/minimal run dev
345
346
  npm --prefix examples/blog install && npm --prefix examples/blog run dev
346
- npm --prefix examples/marketing install && npm --prefix examples/marketing run dev
347
347
  npm --prefix examples/dashboard install && npm --prefix examples/dashboard run dev
348
348
  ```
349
349
 
@@ -352,13 +352,6 @@ npm --prefix examples/dashboard install && npm --prefix examples/dashboard run d
352
352
  - **`examples/blog`** — dynamic routes, tag pages, every config section,
353
353
  fragment-loaded tabs, a form, prewarm, RSS and sitemap, four islands. It
354
354
  intentionally touches every surface of the framework.
355
- - **`examples/marketing`** — the framework's own marketing site: comparison
356
- table, changelog and download pages, long TTLs, prewarm covering every page.
357
- The byte counts on the page are measured from that site's own build output, the
358
- version details are read from the installed package, and the latency numbers
359
- are measured in the browser; there are no invented benchmarks. It is also
360
- bilingual — English at the root, Turkish under `/tr` — which shows how to build
361
- a multi-language site on a framework that ships no i18n of its own.
362
355
  - **`examples/dashboard`** — the opposite axis: per-visitor pages. A signed
363
356
  cookie session, a `private: true` page that never enters the HTML cache, a
364
357
  paginated table fragment, a CSRF-protected form that still works without
package/bin/jskelet.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * JSkelet CLI.
4
4
  *
package/docs/02-mimari.md CHANGED
@@ -36,9 +36,10 @@ JSkelet bu gözlemi mimarinin merkezine alır:
36
36
  ├─ rewrites(beforeFiles) config → proxy ya da req.url değişimi
37
37
  ├─ compression brotli/gzip pazarlığı (kalite 5)
38
38
  ├─ headers statik cache + config headers()
39
- ├─ devGate DEV_TOKEN varsa token yoksa 404
39
+ ├─ devGate gate açıksa ve token yoksa 404
40
40
  ├─ redirects config redirects(), ilk eşleşen kazanır
41
41
  ├─ trailingSlash config trailingSlash: true ise 308
42
+ ├─ robots.txt kullanıcının gövdesine framework Disallow'u ekler
42
43
  ├─ staticPrecompressed build'de üretilmiş .br/.gz kopyalar (kalite 11)
43
44
  ├─ express.static public/ altındaki dosyalar
44
45
  ├─ (dev) devtools yalnızca NODE_ENV=development
@@ -71,6 +72,10 @@ sebebi var ve yer değiştirmek sessiz bozulmalara yol açıyor.
71
72
  bile dışarıya sızdırmamalı. `trailingSlash` config redirects'ten sonra durur,
72
73
  böylece açık kurallar istenen yolu önce görür; kanonik slash biçimi ikinci
73
74
  adımda dayatılır.
75
+ - **`robots.txt`, statikten önce ve sıkıştırmanın içinde.** Uygulamanın
76
+ yazdığı gövde değişmez; framework kendi uçlarının `Disallow` kurallarını
77
+ sona ekler. Sıkıştırılmış bir kopyanın (`Content-Encoding`) üzerine
78
+ yazılmaz — ek düz metne konur, sıkıştırma dışarıda kalır.
74
79
  - **`staticPrecompressed`, `express.static`ten önce.** Build'de üretilmiş
75
80
  `.br`/`.gz` kopyalar varsa onlar servis edilir (brotli kalite 11); yoksa
76
81
  istek altındaki `static`e düşer ve middleware anında sıkıştırır (kalite 5).
@@ -156,7 +156,9 @@ okuduğunda render önbelleğe yazılmaz), ama doğru yer bayrak. Ayrıntılar
156
156
  ## `fragment()` — layout'suz parça
157
157
 
158
158
  Bir bölgeyi tazeleyen uçlar için. Layout basılmaz, yanıt `private, no-store` ve
159
- ETag'siz gider, HTML önbelleğine hiç uğramaz.
159
+ ETag'siz gider, HTML önbelleğine hiç uğramaz. `/_fragment/` öneki
160
+ `robots.txt`'in altına eklenir; parça uçları dizine girmez
161
+ ([04](./04-render-ve-sablonlar.md#robotstxt)).
160
162
 
161
163
  ```js
162
164
  app.get(
@@ -503,6 +503,31 @@ return {
503
503
  `renderHeadMeta(metadata)` fonksiyonu dışa açıktır; layout dışında (ör. bir
504
504
  fragment ya da e-posta) aynı etiketleri üretmek gerekirse kullanılabilir.
505
505
 
506
+ ## robots.txt
507
+
508
+ `robots.txt`'i uygulama yazar: `public/robots.txt` ya da düz bir route.
509
+ Framework bu gövdeyi değiştirmez; başarılı metin yanıtının **altına** bir
510
+ JSkelet notu ve `Disallow` kuralları ekler. Dosya ya da route yoksa framework
511
+ bir `robots.txt` uydurmaz.
512
+
513
+ Eklenen yollar:
514
+
515
+ - `/_jskelet/` — yönetim paneli, uzak görsel proxy, auth handoff
516
+ - `/__jskelet/` — geliştirme araçları
517
+ - `/_fragment/` — layout'suz parça yanıtları
518
+
519
+ Bu öneklerin dışına taşınmış bir uç da eklenir, ama yalnızca gerçekten
520
+ mount edildiyse: `admin.basePath`, `images.remote.path`,
521
+ `auth.crossSubdomainHandoff.path`. `brand.devBasePath` yalnızca
522
+ development'ta yazılır; production'da o yol uygulamanın kendi sayfası
523
+ olabilir.
524
+
525
+ Not, config'teki marka adıyla başlar (`brand.name`, varsayılan `JSkelet`).
526
+ Alttaki grup `User-agent: *` ile birlikte dosyada adı geçen diğer ajanları
527
+ da tekrarlar. Google, belirli bir ajana ait grubu `*` ile birleştirmez;
528
+ aynı ajanın ikinci grubunu birleştirir. Not dosyada zaten varsa ikinci kez
529
+ eklenmez.
530
+
506
531
  ## Dinamik OG görselleri
507
532
 
508
533
  Next.js `ImageResponse` / `opengraph-image.tsx` karşılığı. JSX yok: kart
package/docs/06-cache.md CHANGED
@@ -223,7 +223,11 @@ kabul edilebilir, çünkü fiyat gibi canlı alanlar istemcide WebSocket'ten
223
223
  güncelleniyor.
224
224
 
225
225
  Store LRU'dur: erişilen girdi sona taşınır, sınır (`cache().maxEntries`,
226
- varsayılan 500) aşılınca en eski düşürülür.
226
+ varsayılan 500) aşılınca en eski düşürülür. Config 500'ün üstünü isteyebilir;
227
+ **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
229
+ altında kalan şişman sayfa veya `vary.host` kopyası da bu bütçede LRU ile
230
+ düşer. Tek sayfa 256 MB'den büyükse saklanmaz; yanıt o istekte yine gider.
227
231
 
228
232
  ## Ne önbelleğe yazılır
229
233
 
@@ -1145,9 +1149,9 @@ export default {
1145
1149
  html: { "/": 60, "/haber/:slug": 300 },
1146
1150
  prewarm: {
1147
1151
  onVisit: {
1148
- perPage: 20, // sayfa başına en fazla link
1149
- concurrency: 2, // opsiyonel
1150
- rps: 4, // opsiyonel; 0 = sınırsız
1152
+ perPage: 20, // sayfa başına en fazla link; tavan 20
1153
+ concurrency: 2, // tavan 2
1154
+ rps: 2, // tavan 2; 0 da 2'ye çekilir
1151
1155
  },
1152
1156
  },
1153
1157
  };
@@ -1163,7 +1167,14 @@ Kurallar:
1163
1167
  `private`, degraded veya `no-store` yanıtlar link çıkarmaz.
1164
1168
  - Isıtma isteğinin kendi UA'sı (`brand.prewarmUserAgent`) tetiklemez — sonsuz
1165
1169
  crawl olmaz.
1166
- - Zaten taze olan yollar kuyruğa girmez.
1170
+ - Zaten taze olan yollar kuyruğa girmez. Anahtar `h=host|/yol?` biçimindedir;
1171
+ kontrol vary önekini ve sondaki `?` işaretini de görür. `vary.host` açıkken
1172
+ yalnızca bu isteğin host'u sıcak sayılır.
1173
+ - Bekleyen kuyruk en fazla 64 yoldur; taşan link bu turda alınmaz.
1174
+ - `perPage` 20, `rps` 2, `concurrency` 2 tavanıdır. Daha yükseği (ve `rps: 0`)
1175
+ uyarıyla tavana çekilir. Isıtma isteği loopback'e gider; `vary.host` açıkken
1176
+ public host `x-forwarded-host` ile taşınır, `h=127.0.0.1` diye ikinci girdi
1177
+ açılmaz.
1167
1178
  - `nofollow`, `target="_blank"`, `data-no-prefetch`, `prewarmSkip` ve
1168
1179
  `navigation.exclude` Speculation Rules ile aynı muafiyetleri paylaşır.
1169
1180
  - Query string ısıtılmaz (varsayılan cache politikası query'yi dinamik sayar).
@@ -1306,8 +1317,8 @@ turlar upstream'e iki kat yük bindirirdi.
1306
1317
  `accept-encoding: br, gzip` başlıklarıyla gider; ikincisi sıkıştırılmış gövdenin
1307
1318
  de önbelleğe girmesi için.
1308
1319
 
1309
- `DEV_TOKEN` ayarlıysa ısıtma token'ı çerez olarak taşır; yoksa dev gate tüm
1310
- sayfalara 404 döner ve önbellek hiç dolmaz.
1320
+ Dev gate açıksa ısıtma token'ı çerez olarak taşır; yoksa gate tüm sayfalara
1321
+ 404 döner ve önbellek hiç dolmaz. `DEV_TOKEN` tek başına gate'i açmaz.
1311
1322
 
1312
1323
  Dev panelindeki istek listesi ve terminal, `prewarmUserAgent` taşıyan istekleri
1313
1324
  filtreler: yüzlerce ısıtma isteği görünümü doldurmasın. İlerleme baloncuğun
@@ -293,14 +293,28 @@ static: {
293
293
  }
294
294
  ```
295
295
 
296
+ ## `devGate`
297
+
298
+ **Tip:** `boolean` — **Varsayılan:** `false`
299
+
300
+ Yayına açılmamış ortamı gizler. **`DEV_TOKEN` tek başına siteyi kilitlemez.**
301
+ Paylaşılan bir task tanımı production'a da aynı değişkeni taşıyabilir; o
302
+ durumda ziyaretçi token vermek zorunda kalmaz, site açık kalır.
303
+
304
+ Gate'i açmak için `devGate: true` ya da `DEV_GATE=1`. İkisi de varken token
305
+ taşımayan isteğe 404 döner. `DEV_GATE=0` config'teki açığı da kapatır. Token
306
+ boşsa gate açık olsa da istekler geçer.
307
+
308
+ Ayrıntı: [09-dev-araclari.md](./09-dev-araclari.md).
309
+
296
310
  ## `devGateBypass`
297
311
 
298
312
  **Tip:** `string[]` — **Varsayılan:**
299
313
  `["/api/healthcheck", "/robots.txt", "/sitemap.xml", "/site.webmanifest", "/favicon.ico"]`
300
314
 
301
315
  Dev gate'in hiçbir koşulda kapatmadığı **tam** yollar (önek değil, birebir
302
- eşleşme). `DEV_TOKEN` ayarlı bir ortamda sağlık kontrolünün ve robots
303
- dosyalarının erişilebilir kalması için. Verilirse varsayılanın yerine geçer.
316
+ eşleşme). Gate açıkken sağlık kontrolünün ve robots dosyalarının erişilebilir
317
+ kalması için. Verilirse varsayılanın yerine geçer.
304
318
 
305
319
  Ayrıntı: [09-dev-araclari.md](./09-dev-araclari.md).
306
320
 
@@ -429,7 +443,9 @@ body > footer { view-transition-name: site-footer; }
429
443
  ::view-transition-new(root) { animation-duration: 180ms; }
430
444
  ```
431
445
 
432
- Çalışan hâli `examples/marketing/styles/globals.css` içinde.
446
+ Çalışan bir örnek için Tailwind `@source` ve view-transition CSS'ini kendi
447
+ uygulamanızın `styles/globals.css` dosyasına taşıyın; yukarıdaki bloklar
448
+ başlangıç noktasıdır.
433
449
 
434
450
  **CSP kullanıyorsanız** kurallar satır içi bir `<script type="speculationrules">`
435
451
  olarak basılır; `script-src` politikanızın buna izin vermesi gerekir.
@@ -755,13 +771,16 @@ HTML önbelleğinin girdi sınırı. Girdi başına yüz kilobayt düştüğü i
755
771
  yükseltmek belleği hızla tüketir; on binlerce yollu bir siteyi buradan çözmeye
756
772
  çalışmak yanlış katman, doğru yer `cache().data`.
757
773
 
774
+ **Tavan 800.** Daha yükseği yüklemede uyarıyla 800'e çekilir. Süreç içi HTML +
775
+ sıkıştırılmış gövde ayrıca 256 MB'yi geçemez; bu bütçe config'den yükseltilmez.
776
+
758
777
  ### `cache().data`
759
778
 
760
779
  Upstream veri önbelleği (`withDataCache`). Ayrıntı: [06-cache.md](./06-cache.md).
761
780
 
762
781
  | Alan | Tip | Varsayılan | Anlamı |
763
782
  | --- | --- | --- | --- |
764
- | `maxEntries` | `number` | `10000` | LRU girdi sınırı. JSON, HTML'e göre onlarca kat küçük olduğu için sınır yüksek. |
783
+ | `maxEntries` | `number` | `10000` | LRU girdi sınırı. JSON, HTML'e göre onlarca kat küçük olduğu için sınır yüksek. **Tavan 20000**; üstü uyarıyla kesilir. |
765
784
  | `staleFactor` | `number` | `10` | TTL dolduktan sonra girdinin kaç TTL boyunca daha kullanılabileceği. `0` → bayat servis yok. |
766
785
 
767
786
  ### `cache().trackUpstream`
@@ -1001,13 +1020,13 @@ prewarm: {
1001
1020
  | Alan | Tip | Varsayılan | Anlamı |
1002
1021
  | --- | --- | --- | --- |
1003
1022
  | `onVisit` | `true \| false \| object` | kapalı | Ziyaret tabanlı ısıtma |
1004
- | `onVisit.perPage` | `number` | `20` | Sayfa başına üstten alta en fazla link |
1005
- | `onVisit.concurrency` | `number` | klasik ile aynı | Paralel işçi |
1006
- | `onVisit.rps` | `number` | klasik ile aynı | Saniyedeki tavan; `0` sınırsız |
1023
+ | `onVisit.perPage` | `number` | `20` | Sayfa başına üstten alta en fazla link. **Tavan 20** |
1024
+ | `onVisit.concurrency` | `number` | `2` | Paralel işçi. **Tavan 2** |
1025
+ | `onVisit.rps` | `number` | `2` | Saniyedeki tavan. **Tavan 2**; `0` da 2'ye çekilir |
1007
1026
 
1008
1027
  ```js
1009
1028
  prewarm: {
1010
- onVisit: { perPage: 20, rps: 4 },
1029
+ onVisit: { perPage: 20, rps: 2 },
1011
1030
  }
1012
1031
  ```
1013
1032
 
@@ -1106,7 +1125,8 @@ basılmaz.
1106
1125
  | `PORT` | `startServer` | `3000` | Dinlenecek port. Doluysa süreç başlamaz; `jskelet start|dev --murder` dinleyiciyi öldürür |
1107
1126
  | `HOST` | `startServer` | `::` | Bağlanılacak arayüz. Varsayılan çift yığın dinler (IPv6 + IPv4); IPv6 yoksa `0.0.0.0`'a düşer |
1108
1127
  | `JSKELET_SECRET` | `jskelet/cookies` | — | İmzalı cookie sırrı. `security.cookieSecret` verilmediğinde buradan okunur; ikisi de yoksa imzalı cookie API'si hata verir. [12](./12-panel-ve-oturum.md) |
1109
- | `DEV_TOKEN` | `devGate`, `prewarm` | — | Ayarlıysa token taşımayan her isteğe 404 döner. Isıtma token'ı çerez olarak taşır. [09](./09-dev-araclari.md) |
1128
+ | `DEV_GATE` | `devGate` | kapalı | `1` gate'i açar, `0` config'te açık olsa da kapatır. `DEV_TOKEN` tek başına açmaz. [09](./09-dev-araclari.md) |
1129
+ | `DEV_TOKEN` | `devGate`, `prewarm` | — | Gate açıkken beklenen sır. Yoksa veya gate kapalıysa site açık kalır. Isıtma, gate açıkken token'ı çerez olarak taşır. [09](./09-dev-araclari.md) |
1110
1130
  | `JSKELET_ADMIN` | `createApp` | — | Ayarlıysa yönetim panelini açar; `0` config'te açık olan paneli kapatır. Env config'i ezer, çünkü panel genelde bir arıza sırasında tek seferlik açılır. [06](./06-cache.md) |
1111
1131
  | `JSKELET_LOG_BUCKET` | `logs.s3` | — | Log hedefi: bucket ya da `bucket/prefix` yolu. Credential ile birlikte varsa sink otomatik açılır |
1112
1132
  | `JSKELET_S3_BUCKET` | `logs.s3` | — | `JSKELET_LOG_BUCKET` yoksa bucket; `JSKELET_S3_KEY_PREFIX` ile birleşir |
@@ -298,11 +298,19 @@ hiçbir şey yüklenmez.
298
298
 
299
299
  ## Dev gate — `DEV_TOKEN`
300
300
 
301
- Yayına açılmamış bir ortamı gizlemek için: `DEV_TOKEN` ayarlıyken token
302
- taşımayan **her** isteğe 404 döner.
301
+ Yayına açılmamış bir ortamı gizlemek için. **Framework token'ı zorunlu tutmaz:**
302
+ `DEV_TOKEN` ortamda durması siteyi kilitlemez. Gate'i siz açarsınız.
303
303
 
304
304
  ```bash
305
- DEV_TOKEN=uzun-rastgele-bir-dize npm start
305
+ DEV_GATE=1 DEV_TOKEN=uzun-rastgele-bir-dize npm start
306
+ ```
307
+
308
+ Aynı şey config ile:
309
+
310
+ ```js
311
+ export default {
312
+ devGate: true,
313
+ };
306
314
  ```
307
315
 
308
316
  Erişim:
@@ -322,10 +330,12 @@ Davranış:
322
330
  `/api/healthcheck`, `/robots.txt`, `/sitemap.xml`, `/site.webmanifest`,
323
331
  `/favicon.ico`. Sağlık kontrolünüz farklı bir yolda ise bu listeye eklemeyi
324
332
  unutmayın, aksi hâlde orkestratör 404 görür.
325
- - `DEV_TOKEN` yoksa middleware tamamen devre dışıdır ve üretimde hiçbir maliyeti
326
- olmaz.
327
- - Isıtma kendi sunucusuna istek attığı için token'ı çerez olarak taşır; yoksa tüm
328
- sayfalar 404 alır ve önbellek hiç dolmaz ([06-cache.md](./06-cache.md)).
333
+ - `devGate` kapalıyken (varsayılan) veya `DEV_TOKEN` boşken middleware isteği
334
+ olduğu gibi geçirir. Production task'ına sızmış bir `DEV_TOKEN` ziyaretçiden
335
+ token istemez; açılışta bir uyarı basılır.
336
+ - `DEV_GATE=0` config'te `devGate: true` olsa da gate'i kapatır.
337
+ - Isıtma, gate açıkken token'ı çerez olarak taşır; yoksa tüm sayfalar 404 alır
338
+ ve önbellek hiç dolmaz ([06-cache.md](./06-cache.md)).
329
339
 
330
340
  Gate middleware zincirinde `headers`tan sonra, `redirects`ten **önce** durur:
331
341
  yayına açılmamış bir ortam yönlendirme kurallarını bile dışarıya sızdırmamalı.