jskelet 0.5.5 → 0.6.1

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 (156) hide show
  1. package/AGENTS.md +19 -15
  2. package/CHANGELOG.md +165 -15
  3. package/README.md +16 -21
  4. package/bin/jskelet.mjs +23 -9
  5. package/docs/01-baslangic.md +4 -3
  6. package/docs/02-mimari.md +10 -4
  7. package/docs/03-routing.md +14 -7
  8. package/docs/04-render-ve-sablonlar.md +60 -43
  9. package/docs/05-islands.md +12 -8
  10. package/docs/06-cache.md +18 -7
  11. package/docs/07-yapilandirma.md +69 -27
  12. package/docs/08-build.md +15 -9
  13. package/docs/09-dev-araclari.md +22 -8
  14. package/docs/10-dagitim.md +14 -13
  15. package/docs/11-tasima.md +51 -17
  16. package/docs/12-panel-ve-oturum.md +10 -4
  17. package/docs/README.md +10 -33
  18. package/docs/en/01-getting-started.md +4 -3
  19. package/docs/en/02-architecture.md +12 -6
  20. package/docs/en/03-routing.md +15 -8
  21. package/docs/en/04-rendering.md +71 -59
  22. package/docs/en/05-islands.md +13 -8
  23. package/docs/en/06-caching.md +21 -7
  24. package/docs/en/07-configuration.md +69 -29
  25. package/docs/en/08-build.md +16 -10
  26. package/docs/en/09-dev-tools.md +24 -8
  27. package/docs/en/10-deployment.md +14 -14
  28. package/docs/en/11-migration.md +51 -16
  29. package/docs/en/12-dashboards-and-sessions.md +9 -4
  30. package/docs/en/README.md +10 -35
  31. package/package.json +48 -13
  32. package/src/build/tasks/client.mjs +91 -10
  33. package/src/build/tasks/icons.mjs +11 -1
  34. package/src/client/index.js +2 -2
  35. package/src/compile/codegen.js +4 -0
  36. package/src/compile/compile-all.js +12 -21
  37. package/src/compile/expr.js +5 -0
  38. package/src/compile/parse.js +64 -8
  39. package/src/compile/resolve.js +3 -0
  40. package/src/config/defaults.js +48 -5
  41. package/src/config/index.js +138 -27
  42. package/src/dev-server.mjs +26 -3
  43. package/src/http/cookies-entry.js +1 -0
  44. package/src/http/cookies.js +18 -0
  45. package/src/logo.png +0 -0
  46. package/src/migrate/apply.mjs +262 -0
  47. package/src/migrate/babel.mjs +79 -0
  48. package/src/migrate/classify.mjs +155 -0
  49. package/src/migrate/config.mjs +126 -0
  50. package/src/migrate/fs-walk.mjs +191 -0
  51. package/src/migrate/parse.mjs +26 -0
  52. package/src/migrate/scan.mjs +177 -0
  53. package/src/migrate/transform/expr-source.mjs +168 -0
  54. package/src/migrate/transform/island.mjs +67 -0
  55. package/src/migrate/transform/jsx-to-component.mjs +302 -0
  56. package/src/migrate/transform/jsx-to-jsk.mjs +330 -0
  57. package/src/migrate/transform/page-split.mjs +435 -0
  58. package/src/migrate/write.mjs +81 -0
  59. package/src/migrate.mjs +171 -0
  60. package/src/server/auth/handoff.js +94 -11
  61. package/src/server/create-app.js +37 -10
  62. package/src/server/ejs-adapter.js +59 -0
  63. package/src/server/html-cache.js +178 -32
  64. package/src/server/image-optimizer.js +94 -26
  65. package/src/server/middleware/dev-gate.js +21 -8
  66. package/src/server/middleware/robots-txt.js +341 -0
  67. package/src/server/port-guard.js +255 -0
  68. package/src/server/prewarm.js +137 -51
  69. package/src/server/render.js +30 -10
  70. package/src/server/status-page.js +105 -4
  71. package/src/start.mjs +18 -3
  72. package/src/templates/layout.ejs +8 -28
  73. package/src/templates/layout.jsk +30 -0
  74. package/src/templates/layout.render.js +41 -0
  75. package/src/views/helpers/tags.js +86 -3
  76. package/types/build/resolve-peer.d.mts +13 -0
  77. package/types/client/dom.d.ts +55 -0
  78. package/types/client/form.d.ts +19 -0
  79. package/types/client/index.d.ts +20 -0
  80. package/types/client/registry.d.ts +53 -0
  81. package/types/client/safe-image.d.ts +19 -0
  82. package/types/client/shared-cookie.d.ts +82 -0
  83. package/types/client/store.d.ts +18 -0
  84. package/types/client/swap.d.ts +46 -0
  85. package/types/compile/codegen.d.ts +32 -0
  86. package/types/compile/compile-all.d.ts +42 -0
  87. package/types/compile/errors.d.ts +30 -0
  88. package/types/compile/expr.d.ts +67 -0
  89. package/types/compile/index.d.ts +10 -0
  90. package/types/compile/parse.d.ts +82 -0
  91. package/types/compile/resolve.d.ts +46 -0
  92. package/types/compile/scan-exports.d.ts +9 -0
  93. package/types/config/defaults.d.ts +477 -0
  94. package/types/config/index.d.ts +304 -0
  95. package/types/config/pattern.d.ts +38 -0
  96. package/types/http/control-flow.d.ts +45 -0
  97. package/types/http/cookies-entry.d.ts +5 -0
  98. package/types/http/cookies.d.ts +113 -0
  99. package/types/http/request-cache.d.ts +13 -0
  100. package/types/http/request-context.d.ts +67 -0
  101. package/types/http/shared-cookie.d.ts +73 -0
  102. package/types/index.d.ts +30 -0
  103. package/types/log.d.mts +153 -0
  104. package/types/server/admin/actions.d.ts +16 -0
  105. package/types/server/admin/auth.d.ts +52 -0
  106. package/types/server/admin/event-log.d.ts +38 -0
  107. package/types/server/admin/gate.d.ts +43 -0
  108. package/types/server/admin/inventory.d.ts +40 -0
  109. package/types/server/admin/mount.d.ts +6 -0
  110. package/types/server/admin/router.d.ts +6 -0
  111. package/types/server/admin/snapshot.d.ts +6 -0
  112. package/types/server/assets.d.ts +47 -0
  113. package/types/server/auth/handoff.d.ts +12 -0
  114. package/types/server/cache-deps.d.ts +16 -0
  115. package/types/server/cache-vary.d.ts +30 -0
  116. package/types/server/cloudflare.d.ts +163 -0
  117. package/types/server/create-app.d.ts +25 -0
  118. package/types/server/data-cache.d.ts +116 -0
  119. package/types/server/dev/devtools.d.ts +44 -0
  120. package/types/server/dev/report.d.ts +229 -0
  121. package/types/server/dev/socket.d.ts +17 -0
  122. package/types/server/dev/version-check.d.mts +15 -0
  123. package/types/server/ejs-adapter.d.ts +11 -0
  124. package/types/server/head-hints.d.ts +40 -0
  125. package/types/server/html-cache.d.ts +207 -0
  126. package/types/server/image-optimizer.d.ts +68 -0
  127. package/types/server/logs/access-middleware.d.ts +7 -0
  128. package/types/server/logs/file-sink.d.ts +17 -0
  129. package/types/server/logs/pipeline.d.ts +37 -0
  130. package/types/server/logs/s3-put.d.ts +85 -0
  131. package/types/server/logs/s3-sink.d.ts +26 -0
  132. package/types/server/metadata.d.ts +38 -0
  133. package/types/server/middleware/compression.d.ts +17 -0
  134. package/types/server/middleware/csrf.d.ts +4 -0
  135. package/types/server/middleware/dev-gate.d.ts +2 -0
  136. package/types/server/middleware/headers.d.ts +2 -0
  137. package/types/server/middleware/redirects.d.ts +2 -0
  138. package/types/server/middleware/robots-txt.d.ts +33 -0
  139. package/types/server/middleware/static-precompressed.d.ts +5 -0
  140. package/types/server/middleware/trailing-slash.d.ts +11 -0
  141. package/types/server/middleware/upstream-proxy.d.ts +21 -0
  142. package/types/server/og-image.d.ts +149 -0
  143. package/types/server/port-guard.d.ts +50 -0
  144. package/types/server/prewarm.d.ts +131 -0
  145. package/types/server/redis.d.ts +163 -0
  146. package/types/server/render.d.ts +101 -0
  147. package/types/server/router.d.ts +5 -0
  148. package/types/server/status-page.d.ts +24 -0
  149. package/types/server/upstream-limiter.d.ts +123 -0
  150. package/types/server/upstream-tracking.d.ts +42 -0
  151. package/types/shared/cookie-domain.d.ts +29 -0
  152. package/types/templates/layout.render.d.ts +7 -0
  153. package/types/version.d.mts +10 -0
  154. package/types/views/components/loader.d.ts +5 -0
  155. package/types/views/helpers/html.d.ts +39 -0
  156. package/types/views/helpers/tags.d.ts +127 -0
package/AGENTS.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # AGENTS.md
2
2
 
3
- Bu depo **JSkelet** framework'ünün kaynağıdır: Express 5 + EJS sunucu render,
4
- vanilla JS island'lar, Tailwind v4 ve süreç belleğinde yaşayan HTML TTL cache.
5
- React ve TypeScript yok; düz JavaScript + JSDoc.
3
+ Bu depo **JSkelet** framework'ünün kaynağıdır: Express 5 + build-time `.jsk`
4
+ sunucu render (EJS opsiyonel legacy peer), vanilla JS island'lar, Tailwind v4
5
+ ve süreç belleğinde yaşayan HTML TTL cache. React yok; framework kaynağı düz
6
+ JavaScript + JSDoc. Uygulama client island/entry'leri `.ts` olabilir; paket
7
+ `types/` altında `.d.ts` yayınlar. Sunucu route/hook/config hâlâ Node ESM
8
+ `.js`/`.mjs`.
6
9
 
7
10
  Bir JSkelet **uygulamasında** çalışıyorsan (framework'ün kendisinde değil), aynı
8
11
  kuralların uygulama tarafı karşılıkları için [docs/](./docs/README.md) yeterli;
@@ -26,8 +29,7 @@ zaten tartışılmış:
26
29
 
27
30
  Belgeler iki dilde: Türkçesi `docs/`, İngilizcesi `docs/en/` altında ve
28
31
  dosyalar birebir eşlenik. Bir belgeyi değiştirdiysen karşılığını da güncelle;
29
- pazarlama sitesi (`examples/marketing`) bu dosyaları doğrudan `/docs` altında
30
- 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.
31
33
 
32
34
  ## Doğrulama
33
35
 
@@ -96,16 +98,18 @@ parçalar ayrı ve `no-store` işaretli fragment uçlarında.
96
98
  yalnızca `exports` haritasındaki belirteçler kullanılır (`jskelet`,
97
99
  `jskelet/client`, `jskelet/html`, `jskelet/tags`).
98
100
 
99
- ## EJS tuzakları
101
+ ## `.jsk` tuzakları
100
102
 
101
- - `include` **async**'tir: `await include('partials/x')` yalnızca şablonun kendi
102
- gövdesinde çalışır. Bir `forEach` callback'i içinde derleme hatası verir —
103
- `for` döngüsü kullan.
104
- - `views/components/**` altındaki her named export otomatik olarak şablon local'i
105
- olur; import gerekmez. Bileşenler EJS değil, HTML string döndüren
106
- fonksiyonlardır.
107
- - Şablona giden her kullanıcı verisi `<%= %>` ile ya da `esc()` üzerinden
108
- geçmeli; `<%- %>` yalnızca güvenli bildiğin HTML için.
103
+ - İfade dilinde fonksiyon çağrısı, object/array literal ve atama yok — mantık
104
+ controller veya `views/components/*.js` içinde kalır. Layout’ta `asset()` /
105
+ `hasAsset()` için yerleşik `<Stylesheets />` / `<BodyScripts />` /
106
+ `<JsonLd />` kullan.
107
+ - `{#include "partials/x"}` aynı `data` nesnesini geçer; EJS’teki ikinci-argüman
108
+ locals yok — veriyi controller’da hazırla.
109
+ - `views/components/**` named export’ları PascalCase etiket olur; import yok.
110
+ - Kullanıcı verisi `{{ }}` ile kaçar; `{{{ }}}` yalnızca güvendiğin HTML için.
111
+ - Legacy EJS hâlâ varsa: `include` async’tir (`forEach` içinde `await` yok);
112
+ `<%= %>` / `<%- %>` ayrımına dikkat et; `ejs` opsiyonel peer olarak kurulmalı.
109
113
 
110
114
  ## Tailwind
111
115
 
@@ -118,7 +122,7 @@ sessizce çıktıdan düşer.
118
122
 
119
123
  Framework'ün genel yüzeyini değiştirdiysen (`route()` imzası, hook adları,
120
124
  config alanları, client API'si) `examples/minimal`, `examples/blog` ve
121
- `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ı
122
126
  fark edilen yer orası.
123
127
 
124
128
  ## Windows notları
package/CHANGELOG.md CHANGED
@@ -10,26 +10,175 @@ one is listed under a **Breaking** heading.
10
10
 
11
11
  ### Added
12
12
 
13
- - Local flat `icons/` directory as the exclusive SVG sprite source when present
14
- (`icons.dir`, default `"icons"`): `house.svg` / `house-bold.svg` file names,
15
- XOR with `@phosphor-icons/core` (Phosphor only when the directory is absent).
16
- The hashed sprite still lands under `public/assets/` and is precompressed.
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.
17
31
 
18
32
  ### Fixed
19
33
 
20
- - Open Graph routes with a `.png` suffix (`/og/…/:slug.png`) now escape the
21
- dot for Express 5 / path-to-regexp, so the handler matches again instead of
22
- falling through to the HTML 404.
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 /
95
+ `--murder` hint) instead of only `server exited (code 1)` when the child
96
+ dies during startup.
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).
107
+
108
+ ### Breaking
109
+
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
116
+ `jskelet/layout/ejs` for the legacy file.
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.
23
125
 
24
126
  ### Changed
25
127
 
26
- - Marketing example copy (EN/TR) reflects 0.5.x cache surfaces: host `vary`,
27
- early refresh, classic vs `onVisit` prewarm, local `icons/`, shared cookies,
28
- and `opengraph-image` → `ogHandler` on the migrate table. Pages now serve
29
- per-locale dynamic OG cards at `/og/:locale/:page.png`.
30
- - Marketing changelog page restyled like a release-notes browser: measured
31
- summary cards, search and newest/oldest sort, paginated open release cards
32
- with Latest / Released badges and GitHub links (still driven by `CHANGELOG.md`).
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
153
+ `POST /_jskelet/auth/handoff`.
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.
33
182
 
34
183
  ## [0.5.4] - 2026-09-18
35
184
 
@@ -408,7 +557,8 @@ Initial release.
408
557
  - Documentation under `docs/` and three examples: `minimal`, `blog`,
409
558
  `marketing`.
410
559
 
411
- [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
412
562
  [0.5.4]: https://github.com/ayberkenis/jskelet/compare/v0.5.3...v0.5.4
413
563
  [0.5.3]: https://github.com/ayberkenis/jskelet/compare/v0.5.2...v0.5.3
414
564
  [0.5.2]: https://github.com/ayberkenis/jskelet/compare/v0.5.1...v0.5.2
package/README.md CHANGED
@@ -4,11 +4,13 @@
4
4
  the product.
5
5
 
6
6
  JSkelet renders **complete HTML** on an Express 5 server from build-time
7
- **`.jsk` templates** (EJS still works), adds interactivity through vanilla JS
8
- **islands**, compiles CSS into a **single Tailwind v4 stylesheet**, and instead
9
- of ISR keeps an in-process **HTML TTL cache** with stale-while-revalidate — plus
10
- optional Redis sharing and path-based invalidation. No React, no TypeScript —
11
- plain JavaScript with JSDoc.
7
+ **`.jsk` templates** (optional EJS peer for legacy `.ejs`), adds interactivity
8
+ through vanilla JS **islands**, compiles CSS into a **single Tailwind v4
9
+ stylesheet**, and instead of ISR keeps an in-process **HTML TTL cache** with
10
+ stale-while-revalidate — plus optional Redis sharing and path-based
11
+ invalidation. No React — the framework source is plain JavaScript with JSDoc;
12
+ apps can write client islands and entries in TypeScript, and the published
13
+ package ships declaration files.
12
14
 
13
15
  [![npm version](https://img.shields.io/npm/v/jskelet)](https://www.npmjs.com/package/jskelet)
14
16
  [![Node.js 22+](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org)
@@ -282,9 +284,9 @@ The complete reference — every field, default and failure mode — is
282
284
 
283
285
  | Command | What it does |
284
286
  | --- | --- |
285
- | `jskelet dev` | Watch build plus server, live reload, devtools overlay |
287
+ | `jskelet dev` | Watch build plus server, live reload, devtools overlay. `--murder` kills whatever already holds `PORT` and starts. |
286
288
  | `jskelet build` | Production build: templates → fonts → sprite → CSS → JS → images → manifest → precompress |
287
- | `jskelet start` | Production server; builds first if output is missing |
289
+ | `jskelet start` | Production server; builds first if output is missing. `--murder` same as for `dev`. |
288
290
  | `jskelet init` | Scaffolds a feature-first `.jsk` skeleton into the current directory |
289
291
  | `jskelet generate` | Scaffolds a `feature` / `page` / `island` |
290
292
 
@@ -305,12 +307,13 @@ Anything reachable by a deeper path is internal and may change without notice.
305
307
  ## Deployment
306
308
 
307
309
  The server is a plain Express 5 app, so anything that can run a Node process
308
- works: a `Dockerfile` (see `examples/marketing/Dockerfile`), a systemd unit, or
309
- a PaaS. Run `jskelet build` at image build time, put a reverse proxy in front
310
- for TLS, and expose a health endpoint (the default dev gate bypass list already
311
- includes `/api/healthcheck`, so a route there is reachable in every mode).
312
- Details, including cache sizing behind multiple instances and the optional
313
- 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) /
314
317
  [docs/en/10-deployment.md](./docs/en/10-deployment.md).
315
318
 
316
319
  ## Documentation
@@ -341,7 +344,6 @@ apply to this repository.
341
344
  ```bash
342
345
  npm --prefix examples/minimal install && npm --prefix examples/minimal run dev
343
346
  npm --prefix examples/blog install && npm --prefix examples/blog run dev
344
- npm --prefix examples/marketing install && npm --prefix examples/marketing run dev
345
347
  npm --prefix examples/dashboard install && npm --prefix examples/dashboard run dev
346
348
  ```
347
349
 
@@ -350,13 +352,6 @@ npm --prefix examples/dashboard install && npm --prefix examples/dashboard run d
350
352
  - **`examples/blog`** — dynamic routes, tag pages, every config section,
351
353
  fragment-loaded tabs, a form, prewarm, RSS and sitemap, four islands. It
352
354
  intentionally touches every surface of the framework.
353
- - **`examples/marketing`** — the framework's own marketing site: comparison
354
- table, changelog and download pages, long TTLs, prewarm covering every page.
355
- The byte counts on the page are measured from that site's own build output, the
356
- version details are read from the installed package, and the latency numbers
357
- are measured in the browser; there are no invented benchmarks. It is also
358
- bilingual — English at the root, Turkish under `/tr` — which shows how to build
359
- a multi-language site on a framework that ships no i18n of its own.
360
355
  - **`examples/dashboard`** — the opposite axis: per-visitor pages. A signed
361
356
  cookie session, a `private: true` page that never enters the HTML cache, a
362
357
  paginated table fragment, a CSRF-protected form that still works without
package/bin/jskelet.mjs CHANGED
@@ -2,11 +2,12 @@
2
2
  /**
3
3
  * JSkelet CLI.
4
4
  *
5
- * jskelet dev build watch + sunucu, canlı yenileme, dev overlay
6
- * jskelet build tek seferlik prod build (fontlar, sprite, CSS, JS, görseller)
7
- * jskelet start prod sunucu (build eksikse önce üretir)
5
+ * jskelet dev [--murder] build watch + sunucu, canlı yenileme, dev overlay
6
+ * jskelet build tek seferlik prod build (fontlar, sprite, CSS, JS, görseller)
7
+ * jskelet start [--murder] prod sunucu (build eksikse önce üretir)
8
8
  * jskelet init bulunduğun dizine minimal iskelet kurar
9
9
  * jskelet generate feature / page / island iskeleti
10
+ * jskelet migrate Next.js App Router → JSkelet codemod
10
11
  *
11
12
  * Alt komutlar ayrı süreçlerde çalışır. Sebep: `dev` iki uzun ömürlü süreci
12
13
  * (build watch + sunucu) yönetiyor ve sunucunun ESM resolve hook'larına
@@ -102,15 +103,28 @@ switch (command) {
102
103
  break;
103
104
  }
104
105
 
106
+ case "migrate": {
107
+ const { migrate } = await import("../src/migrate.mjs");
108
+ try {
109
+ await migrate(process.cwd(), rest);
110
+ } catch (error) {
111
+ process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
112
+ process.exit(1);
113
+ }
114
+ break;
115
+ }
116
+
105
117
  default: {
106
118
  const known = command ? `unknown command: ${command}\n\n` : "";
107
119
  process.stderr.write(
108
- `${known}usage: jskelet <dev|build|start|init|generate>\n\n` +
109
- " dev build watch + server (live reload, dev overlay)\n" +
110
- " build production build\n" +
111
- " start production server\n" +
112
- " init scaffold a minimal skeleton in the current directory\n" +
113
- " generate scaffold feature | page | island\n",
120
+ `${known}usage: jskelet <dev|build|start|init|generate|migrate> [options]\n\n` +
121
+ " dev [--murder] build watch + server (live reload, dev overlay)\n" +
122
+ " build production build\n" +
123
+ " start [--murder] production server\n" +
124
+ " init scaffold a minimal skeleton in the current directory\n" +
125
+ " generate scaffold feature | page | island\n" +
126
+ " migrate Next.js App Router → JSkelet codemod (scan | apply | config)\n\n" +
127
+ " --murder if the listen port is busy, kill the listener and start\n",
114
128
  );
115
129
  process.exit(command ? 1 : 0);
116
130
  }
@@ -249,11 +249,12 @@ resolve hook'larına (`--import`) süreç başlangıcında ihtiyaç duyması.
249
249
 
250
250
  | Komut | Ne yapar |
251
251
  | --- | --- |
252
- | `jskelet dev` | Build watch + sunucu, tek terminalde. Canlı yenileme, CSS hot-swap, dev overlay. `NODE_ENV=development`. |
252
+ | `jskelet dev` | Build watch + sunucu, tek terminalde. Canlı yenileme, CSS hot-swap, dev overlay. `NODE_ENV=development`. Port doluysa başlamaz; `--murder` dinleyiciyi öldürüp bağlar. |
253
253
  | `jskelet build` | Tek seferlik prod build: fontlar → ikon sprite → CSS → client JS → görseller → manifest → precompress. `NODE_ENV` verilmemişse `production`. |
254
- | `jskelet start` | Prod sunucu. Build çıktısı yoksa önce üretir. `NODE_ENV` verilmemişse `production`. |
254
+ | `jskelet start` | Prod sunucu. Build çıktısı yoksa önce üretir. `NODE_ENV` verilmemişse `production`. Port davranışı `dev` ile aynı (`--murder`). |
255
255
  | `jskelet init` | Bulunduğun dizine feature-first `.jsk` iskeleti kurar; var olan dosyalara dokunmaz. |
256
256
  | `jskelet generate` | `feature` / `page` / `island` iskeleti üretir. |
257
+ | `jskelet migrate` | Next.js App Router → JSkelet codemod (`scan` / `apply` / `config`). [11-tasima.md](./11-tasima.md). |
257
258
 
258
259
  Bilinmeyen bir komut ya da argümansız çağrı kullanım metnini basar.
259
260
 
@@ -280,7 +281,7 @@ yalnızca bu belirteçleri kullanın:
280
281
  | `jskelet/tags` | `link`, `image`, `icon`, `preloadImage`, `toKebab` |
281
282
  | `jskelet/log` | Konsol çıktısı yardımcıları (`banner`, `event`, `task`, `size`, `ms`, …) |
282
283
  | `jskelet/register` | `node --import jskelet/register` ile alias + uzantı hook'ları |
283
- | `jskelet/layout` | Framework'ün varsayılan `layout.ejs` dosyasının yolu |
284
+ | `jskelet/layout` | Framework'ün varsayılan `layout.jsk` dosyasının yolu |
284
285
 
285
286
  ## Sırada ne var
286
287
 
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
@@ -51,7 +52,7 @@ JSkelet bu gözlemi mimarinin merkezine alır:
51
52
  │ └─ withHtmlCache TTL + stale-while-revalidate
52
53
  │ └─ withUpstreamTracking
53
54
  │ └─ withRequestCache
54
- │ └─ controller → renderPage → EJS
55
+ │ └─ controller → renderPage → .jsk (veya legacy EJS)
55
56
  ├─ 404 → hooks.notFound()
56
57
  └─ hata yönetimi redirect/notFound + 500 fallback
57
58
  ```
@@ -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).
@@ -254,8 +259,9 @@ teşhisi zor sorunlara dönüşüyor.
254
259
 
255
260
  ## Neden bu bağımlılık listesi
256
261
 
257
- Çalışma zamanı bağımlılıkları dörttür: `express`, `ejs`, `esbuild`,
258
- `tailwind-merge`. Geri kalan her şey (Tailwind, PostCSS, lightningcss, sharp,
262
+ Çalışma zamanı bağımlılıkları üçtür: `express`, `esbuild`,
263
+ `tailwind-merge`. `ejs` yalnızca legacy `.ejs` şablonları için opsiyonel peer'dır.
264
+ Geri kalan her şey (Tailwind, PostCSS, lightningcss, sharp,
259
265
  Phosphor ikonları) **opsiyonel peer bağımlılığıdır** ve yoksa ilgili build adımı
260
266
  atlanır.
261
267
 
@@ -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(
@@ -210,7 +212,7 @@ Controller `async (ctx) => sayfa` biçimindedir ve şu alanları döndürebilir:
210
212
 
211
213
  | Alan | Tip | Varsayılan | Anlamı |
212
214
  | --- | --- | --- | --- |
213
- | `view` | `string` | — | `views/` altındaki şablon yolu, uzantısız: `"pages/home"` → `views/pages/home.ejs`. |
215
+ | `view` | `string` | — | `views/` altındaki şablon yolu, uzantısız: `"pages/home"` → `views/pages/home.jsk` (yoksa legacy `.ejs`). |
214
216
  | `data` | `object` | `{}` | Şablona local olarak geçen veriler. |
215
217
  | `metadata` | `object` | `{}` | `<head>` etiketlerine çevrilir; `hooks.metadata()` çıktısının üzerine biner. Şema: [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md). |
216
218
  | `status` | `number` | `200` | HTTP durum kodu. Yalnızca 200 önbelleğe yazılır. |
@@ -326,12 +328,17 @@ hata yöneticisi devreye girer, hatayı loglar ve framework'ün kendi hata sayfa
326
328
  `Cache-Control: no-store` ile döner. Durum kodu hatanın `statusCode` (ya da
327
329
  `status`) alanından okunur; 400–599 aralığında değilse 500 kullanılır.
328
330
 
329
- Framework'ün sayfası bilinçli olarak yalın: durum kodu, tek satır başlık ve tek
330
- satır açıklama. Marka adı, gezinme ya da hata ayrıntısı taşımaz — sunucunun içi
331
- ziyaretçiye açılmaz. Dil `brand.lang`ten gelir (`tr` ve `en` hazır, diğerleri
332
- `en`e düşer).
331
+ **Development** (`NODE_ENV=development`, yani `jskelet dev`): 5xx yanıtlarında
332
+ gömülü 500 sayfası ve `hooks.error()` atlanır; mesaj, yığın izi ve varsa
333
+ `cause` zinciri içeren bir teşhis sayfası döner. 4xx (404 vb.) development'ta
334
+ da her zamanki gibi durum sayfasıdır.
333
335
 
334
- Kendi sayfanı vermek için `hooks.error()`:
336
+ **Production**: framework'ün sayfası bilinçli olarak yalın — durum kodu, tek
337
+ satır başlık ve tek satır açıklama. Marka adı, gezinme ya da hata ayrıntısı
338
+ taşımaz; sunucunun içi ziyaretçiye açılmaz. Dil `brand.lang`ten gelir (`tr` ve
339
+ `en` hazır, diğerleri `en`e düşer).
340
+
341
+ Kendi sayfanı vermek için `hooks.error()` (yalnızca production / 4xx):
335
342
 
336
343
  ```js
337
344
  // jskelet.config.mjs