jskelet 0.5.5 → 0.6.0

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 (149) hide show
  1. package/AGENTS.md +18 -13
  2. package/CHANGELOG.md +95 -0
  3. package/README.md +9 -7
  4. package/bin/jskelet.mjs +24 -10
  5. package/docs/01-baslangic.md +4 -3
  6. package/docs/02-mimari.md +4 -3
  7. package/docs/03-routing.md +11 -6
  8. package/docs/04-render-ve-sablonlar.md +35 -43
  9. package/docs/05-islands.md +12 -8
  10. package/docs/07-yapilandirma.md +40 -18
  11. package/docs/08-build.md +15 -9
  12. package/docs/09-dev-araclari.md +5 -1
  13. package/docs/10-dagitim.md +6 -1
  14. package/docs/11-tasima.md +51 -17
  15. package/docs/12-panel-ve-oturum.md +10 -4
  16. package/docs/README.md +7 -5
  17. package/docs/en/01-getting-started.md +4 -3
  18. package/docs/en/02-architecture.md +5 -5
  19. package/docs/en/03-routing.md +12 -7
  20. package/docs/en/04-rendering.md +47 -59
  21. package/docs/en/05-islands.md +13 -8
  22. package/docs/en/07-configuration.md +40 -20
  23. package/docs/en/08-build.md +16 -10
  24. package/docs/en/09-dev-tools.md +6 -1
  25. package/docs/en/10-deployment.md +6 -1
  26. package/docs/en/11-migration.md +51 -16
  27. package/docs/en/12-dashboards-and-sessions.md +9 -4
  28. package/docs/en/README.md +7 -5
  29. package/package.json +49 -14
  30. package/src/build/tasks/client.mjs +91 -10
  31. package/src/build/tasks/icons.mjs +11 -1
  32. package/src/client/index.js +2 -2
  33. package/src/compile/codegen.js +4 -0
  34. package/src/compile/compile-all.js +12 -21
  35. package/src/compile/expr.js +5 -0
  36. package/src/compile/parse.js +64 -8
  37. package/src/compile/resolve.js +3 -0
  38. package/src/config/defaults.js +12 -2
  39. package/src/config/index.js +1 -1
  40. package/src/dev-server.mjs +26 -3
  41. package/src/http/cookies-entry.js +1 -0
  42. package/src/http/cookies.js +18 -0
  43. package/src/logo.png +0 -0
  44. package/src/migrate/apply.mjs +262 -0
  45. package/src/migrate/babel.mjs +79 -0
  46. package/src/migrate/classify.mjs +155 -0
  47. package/src/migrate/config.mjs +126 -0
  48. package/src/migrate/fs-walk.mjs +191 -0
  49. package/src/migrate/parse.mjs +26 -0
  50. package/src/migrate/scan.mjs +177 -0
  51. package/src/migrate/transform/expr-source.mjs +168 -0
  52. package/src/migrate/transform/island.mjs +67 -0
  53. package/src/migrate/transform/jsx-to-component.mjs +302 -0
  54. package/src/migrate/transform/jsx-to-jsk.mjs +330 -0
  55. package/src/migrate/transform/page-split.mjs +435 -0
  56. package/src/migrate/write.mjs +81 -0
  57. package/src/migrate.mjs +171 -0
  58. package/src/server/auth/handoff.js +94 -11
  59. package/src/server/create-app.js +28 -10
  60. package/src/server/ejs-adapter.js +59 -0
  61. package/src/server/image-optimizer.js +94 -26
  62. package/src/server/port-guard.js +255 -0
  63. package/src/server/render.js +27 -9
  64. package/src/server/status-page.js +105 -4
  65. package/src/start.mjs +18 -3
  66. package/src/templates/layout.ejs +8 -28
  67. package/src/templates/layout.jsk +30 -0
  68. package/src/templates/layout.render.js +41 -0
  69. package/src/views/helpers/tags.js +86 -3
  70. package/types/build/resolve-peer.d.mts +13 -0
  71. package/types/client/dom.d.ts +55 -0
  72. package/types/client/form.d.ts +19 -0
  73. package/types/client/index.d.ts +20 -0
  74. package/types/client/registry.d.ts +53 -0
  75. package/types/client/safe-image.d.ts +19 -0
  76. package/types/client/shared-cookie.d.ts +82 -0
  77. package/types/client/store.d.ts +18 -0
  78. package/types/client/swap.d.ts +46 -0
  79. package/types/compile/codegen.d.ts +32 -0
  80. package/types/compile/compile-all.d.ts +42 -0
  81. package/types/compile/errors.d.ts +30 -0
  82. package/types/compile/expr.d.ts +67 -0
  83. package/types/compile/index.d.ts +10 -0
  84. package/types/compile/parse.d.ts +82 -0
  85. package/types/compile/resolve.d.ts +46 -0
  86. package/types/compile/scan-exports.d.ts +9 -0
  87. package/types/config/defaults.d.ts +449 -0
  88. package/types/config/index.d.ts +299 -0
  89. package/types/config/pattern.d.ts +38 -0
  90. package/types/http/control-flow.d.ts +45 -0
  91. package/types/http/cookies-entry.d.ts +5 -0
  92. package/types/http/cookies.d.ts +113 -0
  93. package/types/http/request-cache.d.ts +13 -0
  94. package/types/http/request-context.d.ts +67 -0
  95. package/types/http/shared-cookie.d.ts +73 -0
  96. package/types/index.d.ts +30 -0
  97. package/types/log.d.mts +153 -0
  98. package/types/server/admin/actions.d.ts +16 -0
  99. package/types/server/admin/auth.d.ts +52 -0
  100. package/types/server/admin/event-log.d.ts +38 -0
  101. package/types/server/admin/gate.d.ts +43 -0
  102. package/types/server/admin/inventory.d.ts +40 -0
  103. package/types/server/admin/mount.d.ts +6 -0
  104. package/types/server/admin/router.d.ts +6 -0
  105. package/types/server/admin/snapshot.d.ts +6 -0
  106. package/types/server/assets.d.ts +47 -0
  107. package/types/server/auth/handoff.d.ts +12 -0
  108. package/types/server/cache-deps.d.ts +16 -0
  109. package/types/server/cache-vary.d.ts +30 -0
  110. package/types/server/cloudflare.d.ts +163 -0
  111. package/types/server/create-app.d.ts +25 -0
  112. package/types/server/data-cache.d.ts +116 -0
  113. package/types/server/dev/devtools.d.ts +44 -0
  114. package/types/server/dev/report.d.ts +229 -0
  115. package/types/server/dev/socket.d.ts +17 -0
  116. package/types/server/dev/version-check.d.mts +15 -0
  117. package/types/server/ejs-adapter.d.ts +11 -0
  118. package/types/server/head-hints.d.ts +40 -0
  119. package/types/server/html-cache.d.ts +173 -0
  120. package/types/server/image-optimizer.d.ts +68 -0
  121. package/types/server/logs/access-middleware.d.ts +7 -0
  122. package/types/server/logs/file-sink.d.ts +17 -0
  123. package/types/server/logs/pipeline.d.ts +37 -0
  124. package/types/server/logs/s3-put.d.ts +85 -0
  125. package/types/server/logs/s3-sink.d.ts +26 -0
  126. package/types/server/metadata.d.ts +38 -0
  127. package/types/server/middleware/compression.d.ts +17 -0
  128. package/types/server/middleware/csrf.d.ts +4 -0
  129. package/types/server/middleware/dev-gate.d.ts +2 -0
  130. package/types/server/middleware/headers.d.ts +2 -0
  131. package/types/server/middleware/redirects.d.ts +2 -0
  132. package/types/server/middleware/static-precompressed.d.ts +5 -0
  133. package/types/server/middleware/trailing-slash.d.ts +11 -0
  134. package/types/server/middleware/upstream-proxy.d.ts +21 -0
  135. package/types/server/og-image.d.ts +149 -0
  136. package/types/server/port-guard.d.ts +50 -0
  137. package/types/server/prewarm.d.ts +128 -0
  138. package/types/server/redis.d.ts +163 -0
  139. package/types/server/render.d.ts +101 -0
  140. package/types/server/router.d.ts +5 -0
  141. package/types/server/status-page.d.ts +24 -0
  142. package/types/server/upstream-limiter.d.ts +123 -0
  143. package/types/server/upstream-tracking.d.ts +42 -0
  144. package/types/shared/cookie-domain.d.ts +29 -0
  145. package/types/templates/layout.render.d.ts +7 -0
  146. package/types/version.d.mts +10 -0
  147. package/types/views/components/loader.d.ts +5 -0
  148. package/types/views/helpers/html.d.ts +39 -0
  149. 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;
@@ -96,16 +99,18 @@ parçalar ayrı ve `no-store` işaretli fragment uçlarında.
96
99
  yalnızca `exports` haritasındaki belirteçler kullanılır (`jskelet`,
97
100
  `jskelet/client`, `jskelet/html`, `jskelet/tags`).
98
101
 
99
- ## EJS tuzakları
100
-
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.
102
+ ## `.jsk` tuzakları
103
+
104
+ - İfade dilinde fonksiyon çağrısı, object/array literal ve atama yok — mantık
105
+ controller veya `views/components/*.js` içinde kalır. Layout’ta `asset()` /
106
+ `hasAsset()` için yerleşik `<Stylesheets />` / `<BodyScripts />` /
107
+ `<JsonLd />` kullan.
108
+ - `{#include "partials/x"}` aynı `data` nesnesini geçer; EJS’teki ikinci-argüman
109
+ locals yok — veriyi controller’da hazırla.
110
+ - `views/components/**` named export’ları PascalCase etiket olur; import yok.
111
+ - Kullanıcı verisi `{{ }}` ile kaçar; `{{{ }}}` yalnızca güvendiğin HTML için.
112
+ - Legacy EJS hâlâ varsa: `include` async’tir (`forEach` içinde `await` yok);
113
+ `<%= %>` / `<%- %>` ayrımına dikkat et; `ejs` opsiyonel peer olarak kurulmalı.
109
114
 
110
115
  ## Tailwind
111
116
 
package/CHANGELOG.md CHANGED
@@ -10,19 +10,110 @@ 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
+
13
38
  - Local flat `icons/` directory as the exclusive SVG sprite source when present
14
39
  (`icons.dir`, default `"icons"`): `house.svg` / `house-bold.svg` file names,
15
40
  XOR with `@phosphor-icons/core` (Phosphor only when the directory is absent).
16
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`).
17
45
 
18
46
  ### Fixed
19
47
 
48
+ - `jskelet dev` prints the real server failure (e.g. port already in use /
49
+ `--murder` hint) instead of only `server exited (code 1)` when the child
50
+ 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
+
20
56
  - Open Graph routes with a `.png` suffix (`/og/…/:slug.png`) now escape the
21
57
  dot for Express 5 / path-to-regexp, so the handler matches again instead of
22
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).
62
+
63
+ ### Breaking
64
+
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
70
+ `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.
23
77
 
24
78
  ### Changed
25
79
 
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
98
+ `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.
26
117
  - Marketing example copy (EN/TR) reflects 0.5.x cache surfaces: host `vary`,
27
118
  early refresh, classic vs `onVisit` prewarm, local `icons/`, shared cookies,
28
119
  and `opengraph-image` → `ogHandler` on the migrate table. Pages now serve
@@ -30,6 +121,10 @@ one is listed under a **Breaking** heading.
30
121
  - Marketing changelog page restyled like a release-notes browser: measured
31
122
  summary cards, search and newest/oldest sort, paginated open release cards
32
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.
33
128
 
34
129
  ## [0.5.4] - 2026-09-18
35
130
 
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
 
package/bin/jskelet.mjs CHANGED
@@ -1,12 +1,13 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
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
@@ -51,7 +51,7 @@ JSkelet bu gözlemi mimarinin merkezine alır:
51
51
  │ └─ withHtmlCache TTL + stale-while-revalidate
52
52
  │ └─ withUpstreamTracking
53
53
  │ └─ withRequestCache
54
- │ └─ controller → renderPage → EJS
54
+ │ └─ controller → renderPage → .jsk (veya legacy EJS)
55
55
  ├─ 404 → hooks.notFound()
56
56
  └─ hata yönetimi redirect/notFound + 500 fallback
57
57
  ```
@@ -254,8 +254,9 @@ teşhisi zor sorunlara dönüşüyor.
254
254
 
255
255
  ## Neden bu bağımlılık listesi
256
256
 
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,
257
+ Çalışma zamanı bağımlılıkları üçtür: `express`, `esbuild`,
258
+ `tailwind-merge`. `ejs` yalnızca legacy `.ejs` şablonları için opsiyonel peer'dır.
259
+ Geri kalan her şey (Tailwind, PostCSS, lightningcss, sharp,
259
260
  Phosphor ikonları) **opsiyonel peer bağımlılığıdır** ve yoksa ilgili build adımı
260
261
  atlanır.
261
262
 
@@ -210,7 +210,7 @@ Controller `async (ctx) => sayfa` biçimindedir ve şu alanları döndürebilir:
210
210
 
211
211
  | Alan | Tip | Varsayılan | Anlamı |
212
212
  | --- | --- | --- | --- |
213
- | `view` | `string` | — | `views/` altındaki şablon yolu, uzantısız: `"pages/home"` → `views/pages/home.ejs`. |
213
+ | `view` | `string` | — | `views/` altındaki şablon yolu, uzantısız: `"pages/home"` → `views/pages/home.jsk` (yoksa legacy `.ejs`). |
214
214
  | `data` | `object` | `{}` | Şablona local olarak geçen veriler. |
215
215
  | `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
216
  | `status` | `number` | `200` | HTTP durum kodu. Yalnızca 200 önbelleğe yazılır. |
@@ -326,12 +326,17 @@ hata yöneticisi devreye girer, hatayı loglar ve framework'ün kendi hata sayfa
326
326
  `Cache-Control: no-store` ile döner. Durum kodu hatanın `statusCode` (ya da
327
327
  `status`) alanından okunur; 400–599 aralığında değilse 500 kullanılır.
328
328
 
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).
329
+ **Development** (`NODE_ENV=development`, yani `jskelet dev`): 5xx yanıtlarında
330
+ gömülü 500 sayfası ve `hooks.error()` atlanır; mesaj, yığın izi ve varsa
331
+ `cause` zinciri içeren bir teşhis sayfası döner. 4xx (404 vb.) development'ta
332
+ da her zamanki gibi durum sayfasıdır.
333
333
 
334
- Kendi sayfanı vermek için `hooks.error()`:
334
+ **Production**: framework'ün sayfası bilinçli olarak yalın — durum kodu, tek
335
+ satır başlık ve tek satır açıklama. Marka adı, gezinme ya da hata ayrıntısı
336
+ taşımaz; sunucunun içi ziyaretçiye açılmaz. Dil `brand.lang`ten gelir (`tr` ve
337
+ `en` hazır, diğerleri `en`e düşer).
338
+
339
+ Kendi sayfanı vermek için `hooks.error()` (yalnızca production / 4xx):
335
340
 
336
341
  ```js
337
342
  // jskelet.config.mjs
@@ -51,7 +51,7 @@ controller data → import edilmiş render(data, helpers) → HTML
51
51
  {/if}
52
52
 
53
53
  {#each items as item, i}
54
- <li data-i="{{ i }}">{{ item }}</li>
54
+ <li :data-i="i">{{ item }}</li>
55
55
  {/each}
56
56
 
57
57
  <Link href="/" text="Home" />
@@ -99,19 +99,30 @@ code --install-extension extensions/vscode-jsk
99
99
 
100
100
  Ayrıntılar uzantı README'sinde.
101
101
 
102
+ ### Yerleşik layout etiketleri
103
+
104
+ `.jsk` ifade dilinde `asset()` / `hasAsset()` çağrılamaz. Layout’ta stylesheet,
105
+ script ve JSON-LD döngüleri için yerleşikler:
106
+
107
+ | Etiket | Props | Çıktı |
108
+ | --- | --- | --- |
109
+ | `Stylesheets` | `styles` | `app.css` + sayfa sheet’leri (`data-jskelet-css`) |
110
+ | `BodyScripts` | `entries`, `devtools`, `devBasePath` | `main.js`, entry’ler, isteğe bağlı overlay |
111
+ | `JsonLd` | `items` (`structuredData`) | `application/ld+json` script’leri |
112
+
102
113
  ### EJS ile birlikte yaşam
103
114
 
104
115
  Aynı `view` id için derlenmiş `.jsk` varsa o kullanılır; yoksa `.ejs` dosyası
105
- EJS ile render edilir. Mevcut uygulamalar değişmeden çalışır. `jskelet init`
106
- yeni iskeleti `.jsk` ile kurar.
116
+ **yalnızca `ejs` peer’i kuruluysa** render edilir. `jskelet init` yeni iskeleti
117
+ `.jsk` ile kurar.
107
118
 
108
- ## EJS motoru (legacy)
119
+ ## EJS motoru (legacy peer)
109
120
 
110
- EJS hâlâ desteklenir. Motor ilk render'da bir kez kurulur; bileşen taraması
111
- dosya sistemine dokunduğu için her istekte yapılamaz ve config yüklenmeden
112
- hesaplanamaz.
121
+ EJS opsiyonel peer bağımlılıktır (`npm i ejs`). `.jsk`-only uygulamalar kurmak
122
+ zorunda değildir. Bir `.ejs` view veya layout istendiğinde paket uygulamadan
123
+ yüklenir; yoksa göç yolunu gösteren bir hata fırlatılır.
113
124
 
114
- Ayarlar:
125
+ Motor ilk EJS render’da bir kez kurulur. Ayarlar:
115
126
 
116
127
  | Ayar | Değer | Sebebi |
117
128
  | --- | --- | --- |
@@ -120,9 +131,7 @@ Ayarlar:
120
131
  | `rmWhitespace` | `true` | çıktı boyutu |
121
132
  | `async` | `true` | şablon içinde `await` kullanılabilir |
122
133
 
123
- Gömülü kullanımlar (test, script) için `resetRenderEngine()` dışa açık: bileşen
124
- dosyaları değişince kaydı yeniler. Dev sunucusu süreci yeniden başlattığı için
125
- normal akışta gerekmez.
134
+ Gömülü kullanımlar (test, script) için `resetRenderEngine()` dışa açık.
126
135
 
127
136
  ## Layout
128
137
 
@@ -130,50 +139,33 @@ normal akışta gerekmez.
130
139
 
131
140
  1. `jskelet.config.mjs` → `layout` verilmişse o kullanılır. Yol, **views
132
141
  dizininin üst dizinine** göre çözülür: `views` varsayılansa
133
- `layout: "views/ozel.ejs"` → `<root>/views/ozel.ejs`.
142
+ `layout: "views/ozel.jsk"` → `<root>/views/ozel.jsk`.
134
143
  2. Verilmemişse `views/layout.jsk` (derlenmiş) varsa o kullanılır.
135
- 3. Yoksa `views/layout.ejs` varsa o kullanılır.
144
+ 3. Yoksa `views/layout.ejs` varsa o kullanılır (EJS peer gerekir).
136
145
  4. O da yoksa framework'ün kendi minimal layout'u kullanılır
137
- (`node_modules/jskelet/src/templates/layout.ejs`, ayrıca
138
- `jskelet/layout` belirteciyle de erişilebilir).
146
+ (`node_modules/jskelet/src/templates/layout.jsk`, `jskelet/layout`;
147
+ legacy kopya `jskelet/layout/ejs`).
139
148
 
140
149
  Üçüncü seçenek yeni bir projenin tek route ile çalışabilmesi için var. Kendi
141
- layout'unuza geçmenin en pratik yolu o dosyayı `views/layout.ejs` olarak
150
+ layout'unuza geçmenin en pratik yolu o dosyayı `views/layout.jsk` olarak
142
151
  kopyalamaktır.
143
152
 
144
153
  ### Framework'ün varsayılan layout'u
145
154
 
146
- ```ejs
155
+ ```jsk
147
156
  <!DOCTYPE html>
148
- <html lang="<%= lang %>">
157
+ <html :lang="lang">
149
158
  <head>
150
159
  <meta charset="utf-8">
151
160
  <meta name="viewport" content="width=device-width, initial-scale=1">
152
- <%- extraHead %>
153
- <% if (hasAsset('app.css')) { %>
154
- <link rel="stylesheet" href="<%= asset('app.css') %>" data-jskelet-css="app.css">
155
- <% } %>
156
- <% styles.forEach(function (sheet) { %>
157
- <% if (hasAsset(sheet)) { %>
158
- <link rel="stylesheet" href="<%= asset(sheet) %>" data-jskelet-css="<%= sheet %>">
159
- <% } %>
160
- <% }); %>
161
- <%- headMeta %>
162
- <% structuredData.forEach(function (item) { %>
163
- <script type="application/ld+json"><%- jsonScript(item) %></script>
164
- <% }); %>
161
+ {{{ extraHead }}}
162
+ <Stylesheets :styles="styles" />
163
+ {{{ headMeta }}}
164
+ <JsonLd :items="structuredData" />
165
165
  </head>
166
- <body class="<%= bodyClass %>">
167
- <%- body %>
168
- <% if (hasAsset('main.js')) { %>
169
- <script type="module" src="<%= asset('main.js') %>"></script>
170
- <% } %>
171
- <% entries.forEach(function (entry) { %>
172
- <script type="module" src="<%= asset(entry) %>"></script>
173
- <% }); %>
174
- <% if (devtools) { %>
175
- <script type="module" src="<%= devBasePath %>/overlay.js"></script>
176
- <% } %>
166
+ <body :class="bodyClass">
167
+ {{{ body }}}
168
+ <BodyScripts :entries="entries" :devtools="devtools" :devBasePath="devBasePath" />
177
169
  </body>
178
170
  </html>
179
171
  ```
@@ -187,7 +179,7 @@ Dikkat edilecek noktalar:
187
179
  sheet'leri de aynı şekilde basılır. Build çalışmadıysa `hasAsset` false olur
188
180
  ve etiket hiç basılmaz.
189
181
  - **`hasAsset` kontrolleri** build eksikken sayfanın 404 veren dosyaları
190
- istememesini sağlar.
182
+ istememesini sağlar (`Stylesheets` / `BodyScripts` içinde).
191
183
  - **Devtools script'i** yalnızca `NODE_ENV=development` iken basılır; prod
192
184
  çıktısında hiç yoktur.
193
185
 
@@ -118,25 +118,29 @@ Pratik sonucu: bir modal'ı `hidden` başlatabilirsiniz, island'ı yine bağlan
118
118
  ```
119
119
  client/
120
120
  ├── entries/
121
- │ ├── main.js her sayfada yüklenen ortak bootstrap
121
+ │ ├── main.js her sayfada yüklenen ortak bootstrap (veya main.ts)
122
122
  │ └── chart.js yalnızca isteyen sayfalarda
123
123
  └── islands/
124
- ├── counter.js
124
+ ├── counter.ts .js veya .ts
125
125
  └── chart.js
126
126
  ```
127
127
 
128
- `client/entries/*.js` içindeki **her dosya bir esbuild entry'sidir**. `main.js`
129
- layout tarafından her sayfada yüklenir (manifest'te varsa). Ek entry'ler
130
- yalnızca onları isteyen sayfalarda yüklenir:
128
+ `client/entries/*.{js,ts,mts}` içindeki **her dosya bir esbuild entry'sidir**.
129
+ `main.js` (veya `main.ts`) layout tarafından her sayfada yüklenir (manifest'te
130
+ varsa). Ek entry'ler yalnızca onları isteyen sayfalarda yüklenir. Aynı stem için
131
+ iki uzantı (`main.js` + `main.ts`) build hatasıdır.
131
132
 
132
133
  ```js
133
- // controller
134
+ // controller — manifest anahtarı her zaman *.js kalır
134
135
  return { view: "pages/markets", entries: ["chart.js"] };
135
136
  ```
136
137
 
137
138
  Layout `entries` dizisindeki her adı `asset(entry)` ile çözüp bir
138
- `<script type="module">` basar. Ad manifest anahtarıdır, yani dosya adının
139
- kendisi (`chart.js`), hash'li hâli değil.
139
+ `<script type="module">` basar. Ad manifest anahtarıdır (`chart.js`), kaynak
140
+ dosya `chart.ts` olsa bile hash'siz anahtar `.js` kalır.
141
+
142
+ Paylaşılan `@/lib` modülleri sunucuda da import ediliyorsa **`.js` kalsın** —
143
+ Node runtime `.ts` çözmez; `.ts` yalnızca esbuild client hattında derlenir.
140
144
 
141
145
  Kod bölme (`splitting: true`) açık: iki entry'nin paylaştığı modüller ortak bir
142
146
  chunk'a çıkar ve iki kez indirilmez.
@@ -51,7 +51,7 @@ export default {
51
51
  lang: "tr",
52
52
  },
53
53
 
54
- layout: "views/layout.ejs",
54
+ layout: "views/layout.jsk",
55
55
  routes: ["./routes/10-pages.mjs", "./routes/99-catch-all.mjs"],
56
56
  trailingSlash: false,
57
57
 
@@ -208,26 +208,32 @@ session id taşımak için handoff köprüsünü açar.
208
208
 
209
209
  | Alan | Tip | Varsayılan | Anlamı |
210
210
  | --- | --- | --- | --- |
211
- | `crossSubdomainHandoff` | `boolean \| object` | `false` | `true` veya `{ ttlSeconds?, path?, maxValueBytes? }` → `POST /_jskelet/auth/handoff` + `?handoff=` redeem |
211
+ | `crossSubdomainHandoff` | `boolean \| object` | `false` | Açıkken `POST /_jskelet/auth/handoff` + `?handoff=` redeem. Object: `allowedCookieNames` (zorunlu), `ttlSeconds?`, `path?`, `maxValueBytes?`, `maxPendingTickets?`, `maxMintsPerIpPerMinute?` |
212
212
 
213
213
  ```js
214
214
  auth: {
215
- crossSubdomainHandoff: { ttlSeconds: 60 },
215
+ crossSubdomainHandoff: {
216
+ allowedCookieNames: ["sid"],
217
+ ttlSeconds: 60,
218
+ },
216
219
  },
217
220
  ```
218
221
 
219
- Ayrıntı ve `window.name` yedeği: [12-panel-ve-oturum.md](./12-panel-ve-oturum.md).
222
+ Mint uç noktası CSRF middleware'inden **sonra** mount edilir (origin kontrolü).
223
+ Cookie adı allowlist dışındaysa veya RFC 6265 token değilse 400. Ayrıntı:
224
+ [12-panel-ve-oturum.md](./12-panel-ve-oturum.md).
220
225
 
221
226
  ## `layout`
222
227
 
223
228
  **Tip:** `string` — **Varsayılan:** yok (otomatik çözüm)
224
229
 
225
- Layout `.ejs` dosyasının yolu. Verilen değer **views dizininin üst dizinine**
226
- göre çözülür, yani varsayılan `views` ile `"views/ozel.ejs"` →
227
- `<root>/views/ozel.ejs`.
230
+ Layout dosyasının yolu (`.jsk` veya legacy `.ejs`). Verilen değer **views
231
+ dizininin üst dizinine** göre çözülür, yani varsayılan `views` ile
232
+ `"views/ozel.jsk"` → `<root>/views/ozel.jsk`.
228
233
 
229
- Verilmezse sırayla: `views/layout.jsk`, `views/layout.ejs`, yoksa framework'ün
230
- minimal layout'u. Ayrıntı: [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md).
234
+ Verilmezse sırayla: `views/layout.jsk`, `views/layout.ejs` (legacy), yoksa
235
+ framework'ün `src/templates/layout.jsk` varsayılanı. Ayrıntı:
236
+ [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md).
231
237
 
232
238
  ## `routes`
233
239
 
@@ -328,7 +334,7 @@ var.
328
334
  | `trustProxy` | `boolean` | `true` | Express'in `trust proxy` ayarı. Ters proxy arkasında doğru protokol ve istemci IP'si için gerekli. |
329
335
  | `cookieSecret` | `string \| null` | `null` | İmzalı cookie sırrı. Verilmezse `JSKELET_SECRET` okunur. |
330
336
  | `csrf.enabled` | `boolean` | `true` | Origin/`Sec-Fetch-Site` kontrolü. |
331
- | `csrf.token` | `boolean` | `false` | Çift gönderim token'ı katmanı. |
337
+ | `csrf.token` | `boolean` | `false` | Çift gönderim token'ı katmanı. Cookie oturumlu formlarda **açın**. |
332
338
  | `csrf.allowedOrigins` | `string[]` | `[]` | Kendi host'umuzun yanında kabul edilen origin'ler. |
333
339
  | `csrf.exclude` | `string[]` | `[]` | Kontrolden muaf yollar; `source` desen sözdizimi. |
334
340
  | `csrf.cookieName` | `string` | `"csrf_token"` | Token cookie'sinin adı. |
@@ -336,14 +342,17 @@ var.
336
342
  | `csrf.headerName` | `string` | `"x-csrf-token"` | Token'ın kabul edildiği başlık. |
337
343
 
338
344
  `trustProxy` doğrudan internete açık bir sunucuda **kapatılmalı**: açıkken
339
- istemci kendi `X-Forwarded-For` başlığını uydurabilir ve rate limit ile audit
340
- log yanlış adresi görür.
345
+ istemci kendi `X-Forwarded-For` / `X-Forwarded-Proto` / Host başlığını
346
+ uydurabilir; rate limit, admin IP allowlist, Secure cookie ve cache `vary.host`
347
+ yanlış adresi görür. Ters proxy (nginx, Caddy, Cloudflare) arkasındaysa `true`
348
+ doğru varsayılandır.
341
349
 
342
350
  CSRF kontrolü yalnızca çapraz site olduğu **belli** olan istekleri reddeder —
343
351
  `Origin` uyuşmuyorsa ya da `Sec-Fetch-Site: cross-site` geldiyse. İkisi de yoksa
344
352
  istek geçer, çünkü tarayıcılar çapraz origin bir POST'ta `Origin`'i her zaman
345
- gönderirken webhook'lar hiç göndermez. Yine de tarayıcıdan gelmeyen uçları
346
- `csrf.exclude` listesine yazmak niyeti okunur kılıyor.
353
+ gönderirken webhook'lar hiç göndermez. Cookie ile oturum açan panel/form
354
+ uygulamalarında `csrf.token: true` + `csrfField()` ikinci katmandır; webhook
355
+ uçlarını `csrf.exclude` listesine yazın.
347
356
 
348
357
  ## `navigation`
349
358
 
@@ -540,7 +549,9 @@ icons: {
540
549
 
541
550
  `false` verilirse görsel adımı hiç çalışmaz. Build adımı `sharp` gerektirir ve
542
551
  watch turunda hiç çalışmaz. Remote açıksa `sharp` **runtime**'da da gerekir;
543
- yoksa optimizer kaynak URL'ye 302 yönlendirir. Ayrıntı: [08-build.md](./08-build.md).
552
+ yoksa optimizer kaynak URL'ye 302 yönlendirir. Fetch, redirect'leri otomatik
553
+ takip etmez: her hop `allowHosts` ve private adres kontrolünden geçer.
554
+ Ayrıntı: [08-build.md](./08-build.md).
544
555
 
545
556
  ```js
546
557
  images: {
@@ -573,6 +584,9 @@ clientEnv: ["PUBLIC_WS_URL", "PUBLIC_CDN_ORIGIN"]
573
584
  ```
574
585
 
575
586
  **Buraya gizli anahtar koymayın** — değerler bundle'da düz metin olarak durur.
587
+ İsimlerinde `SECRET`, `PASSWORD`, `TOKEN`, `API_KEY`, `PRIVATE` vb. geçen
588
+ anahtarlar build sırasında **reddeder** (`PUBLIC` / `PUBLISHABLE` içerenler
589
+ muaf).
576
590
 
577
591
  ## `headers()`
578
592
 
@@ -581,7 +595,8 @@ clientEnv: ["PUBLIC_WS_URL", "PUBLIC_CDN_ORIGIN"]
581
595
 
582
596
  Yol desenine göre yanıt başlıkları. Framework yalnızca statik dosyalara uzun
583
597
  ömürlü cache yazar; bunun dışındaki her başlık (CSP, COOP, HSTS,
584
- X-Frame-Options…) buradan gelir ve varsayılanların üstüne biner.
598
+ X-Frame-Options…) buradan gelir ve varsayılanların üstüne biner. Üretim
599
+ sitelerinde en azından aşağıdaki güvenlik başlıklarını tanımlayın.
585
600
 
586
601
  Eşleşen **tüm** kurallar uygulanır (redirect'lerin aksine ilk eşleşmede
587
602
  durulmaz), sırayla; aynı başlığı iki kural yazarsa sonraki kazanır.
@@ -596,11 +611,18 @@ async headers() {
596
611
  source: "/:path*",
597
612
  headers: [
598
613
  { key: "X-Frame-Options", value: "SAMEORIGIN" },
614
+ { key: "X-Content-Type-Options", value: "nosniff" },
599
615
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
616
+ {
617
+ key: "Permissions-Policy",
618
+ value: "camera=(), microphone=(), geolocation=()",
619
+ },
600
620
  {
601
621
  key: "Content-Security-Policy",
602
- value: "default-src 'self'; img-src 'self' https://cdn.ornek.com data:",
622
+ value: "default-src 'self'; img-src 'self' https://cdn.ornek.com data:; script-src 'self'",
603
623
  },
624
+ // Yalnızca HTTPS terminasyonu sizin kontrolünüzdeyse:
625
+ // { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
604
626
  ],
605
627
  },
606
628
  {
@@ -1081,7 +1103,7 @@ basılmaz.
1081
1103
  | Değişken | Kim okur | Varsayılan | Anlamı |
1082
1104
  | --- | --- | --- | --- |
1083
1105
  | `NODE_ENV` | her yer | `production` (start/build), `development` (dev) | Dev overlay, EJS cache, manifest yeniden okuma, route hata davranışı ve prewarm varsayılanlarını belirler. `jskelet dev` bunu kendisi ayarlar — `cross-env` gerekmez. |
1084
- | `PORT` | `startServer` | `3000` | Dinlenecek port |
1106
+ | `PORT` | `startServer` | `3000` | Dinlenecek port. Doluysa süreç başlamaz; `jskelet start|dev --murder` dinleyiciyi öldürür |
1085
1107
  | `HOST` | `startServer` | `::` | Bağlanılacak arayüz. Varsayılan çift yığın dinler (IPv6 + IPv4); IPv6 yoksa `0.0.0.0`'a düşer |
1086
1108
  | `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) |
1087
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) |