jskelet 0.2.5 → 0.3.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 (106) hide show
  1. package/AGENTS.md +132 -132
  2. package/CHANGELOG.md +403 -383
  3. package/LICENSE +21 -21
  4. package/bin/jskelet.mjs +103 -103
  5. package/docs/01-baslangic.md +285 -285
  6. package/docs/02-mimari.md +293 -287
  7. package/docs/03-routing.md +486 -480
  8. package/docs/04-render-ve-sablonlar.md +490 -490
  9. package/docs/05-islands.md +482 -482
  10. package/docs/06-cache.md +1231 -1209
  11. package/docs/07-yapilandirma.md +44 -21
  12. package/docs/08-build.md +366 -366
  13. package/docs/09-dev-araclari.md +335 -335
  14. package/docs/10-dagitim.md +329 -329
  15. package/docs/11-tasima.md +1 -0
  16. package/docs/12-panel-ve-oturum.md +384 -384
  17. package/docs/README.md +105 -105
  18. package/docs/en/01-getting-started.md +292 -292
  19. package/docs/en/02-architecture.md +311 -305
  20. package/docs/en/03-routing.md +503 -497
  21. package/docs/en/04-rendering.md +504 -504
  22. package/docs/en/05-islands.md +492 -492
  23. package/docs/en/06-caching.md +1197 -1198
  24. package/docs/en/07-configuration.md +1009 -986
  25. package/docs/en/08-build.md +383 -383
  26. package/docs/en/09-dev-tools.md +342 -342
  27. package/docs/en/10-deployment.md +332 -332
  28. package/docs/en/11-migration.md +360 -359
  29. package/docs/en/12-dashboards-and-sessions.md +392 -392
  30. package/docs/en/README.md +112 -112
  31. package/package.json +102 -102
  32. package/src/build/ensure-build.mjs +15 -15
  33. package/src/build/paths.mjs +143 -143
  34. package/src/build/resolve-peer.mjs +36 -36
  35. package/src/build/tasks/client.mjs +268 -268
  36. package/src/build/tasks/css.mjs +124 -124
  37. package/src/build/tasks/fonts.mjs +146 -146
  38. package/src/build/tasks/icons.mjs +224 -224
  39. package/src/build/tasks/images.mjs +244 -244
  40. package/src/build/tasks/precompress.mjs +78 -78
  41. package/src/client/{cache-panel → admin}/i18n.js +756 -670
  42. package/src/client/{cache-panel → admin}/login.html +74 -74
  43. package/src/client/{cache-panel → admin}/panel.css +804 -756
  44. package/src/client/admin/panel.html +486 -0
  45. package/src/client/{cache-panel → admin}/panel.js +1242 -915
  46. package/src/client/devtools/report.html +185 -185
  47. package/src/client/devtools/report.js +725 -725
  48. package/src/client/dom.js +95 -95
  49. package/src/client/form.js +192 -192
  50. package/src/client/index.js +35 -35
  51. package/src/client/registry.js +297 -297
  52. package/src/client/safe-image.js +91 -91
  53. package/src/client/store.js +36 -36
  54. package/src/client/swap.js +188 -188
  55. package/src/config/defaults.js +16 -7
  56. package/src/config/index.js +38 -22
  57. package/src/config/pattern.js +107 -107
  58. package/src/http/control-flow.js +71 -71
  59. package/src/http/cookies.js +257 -257
  60. package/src/http/request-cache.js +46 -46
  61. package/src/http/request-context.js +162 -162
  62. package/src/index.js +83 -83
  63. package/src/init.mjs +221 -221
  64. package/src/log.mjs +58 -0
  65. package/src/runtime/alias-hooks.mjs +119 -119
  66. package/src/runtime/register.mjs +4 -4
  67. package/src/server/admin/actions.js +229 -0
  68. package/src/server/admin/auth.js +125 -0
  69. package/src/server/admin/event-log.js +151 -0
  70. package/src/server/admin/gate.js +209 -0
  71. package/src/server/admin/inventory.js +188 -0
  72. package/src/server/admin/mount.js +56 -0
  73. package/src/server/admin/router.js +216 -0
  74. package/src/server/admin/snapshot.js +126 -0
  75. package/src/server/assets.js +147 -147
  76. package/src/server/cache-deps.js +42 -42
  77. package/src/server/cloudflare.js +607 -607
  78. package/src/server/create-app.js +295 -291
  79. package/src/server/data-cache.js +462 -462
  80. package/src/server/dev/report.js +369 -369
  81. package/src/server/dev/socket.js +170 -170
  82. package/src/server/dev/version-check.mjs +139 -139
  83. package/src/server/html-cache.js +817 -817
  84. package/src/server/metadata.js +102 -102
  85. package/src/server/middleware/compression.js +205 -205
  86. package/src/server/middleware/csrf.js +134 -134
  87. package/src/server/middleware/dev-gate.js +62 -62
  88. package/src/server/middleware/headers.js +37 -37
  89. package/src/server/middleware/redirects.js +32 -32
  90. package/src/server/middleware/static-precompressed.js +100 -100
  91. package/src/server/middleware/trailing-slash.js +53 -0
  92. package/src/server/middleware/upstream-proxy.js +141 -141
  93. package/src/server/prewarm.js +601 -601
  94. package/src/server/redis.js +569 -569
  95. package/src/server/router.js +128 -128
  96. package/src/server/status-page.js +164 -164
  97. package/src/server/upstream-limiter.js +376 -376
  98. package/src/server/upstream-tracking.js +166 -166
  99. package/src/start.mjs +7 -7
  100. package/src/templates/layout.ejs +44 -44
  101. package/src/version.mjs +31 -31
  102. package/src/views/components/loader.js +85 -85
  103. package/src/views/helpers/html.js +102 -102
  104. package/src/views/helpers/tags.js +245 -245
  105. package/src/client/cache-panel/panel.html +0 -308
  106. package/src/server/cache-panel.js +0 -759
@@ -1,166 +1,166 @@
1
- /**
2
- * Render başına upstream API hatalarını toplar.
3
- *
4
- * `render.js` her sayfayı bu bağlam içinde üretir. Böylece HTML önbelleği "bu
5
- * çıktı eksik veriyle üretildi" bilgisine sahip olur ve bozuk sayfayı saklamaz;
6
- * `notFound()` de geçici bir hataya denk geldiğinde 404 olmaktan çıkar.
7
- *
8
- * Bilgi iki yoldan gelir:
9
- *
10
- * 1. **Otomatik** — `trackUpstreamFetch()` `globalThis.fetch`i sarar ve
11
- * geçici hataları (429, 5xx, ağ) kendiliğinden bildirir. `createApp()`
12
- * bunu açılışta kurar, yani hiçbir uygulama kodu gerekmez.
13
- * 2. **Elle** — `fetch` kullanmayan bir istemci (veritabanı sürücüsü, gRPC,
14
- * SDK) için:
15
- *
16
- * import { reportUpstreamFailure } from "jskelet";
17
- *
18
- * if (!response.ok) {
19
- * reportUpstreamFailure({ status: response.status, path: url });
20
- * }
21
- *
22
- * İki yol aynı hatayı bildirirse tekilleştirilir.
23
- *
24
- * Sarmalayıcı aynı zamanda hız freninin durduğu yerdir
25
- * (`upstream-limiter.js`): kotayı harcayan şey sayfa isteği değil, buradan
26
- * geçen çağrı. Fren varsayılan olarak kapalı.
27
- */
28
- import { AsyncLocalStorage } from "node:async_hooks";
29
- import { limitUpstream, noteUpstreamResponse } from "./upstream-limiter.js";
30
-
31
- /**
32
- * @typedef {{ status: number, path: string }} UpstreamFailure
33
- * `status: 0` ağ hatası anlamına gelir (yanıt hiç gelmedi).
34
- */
35
-
36
- /** @type {AsyncLocalStorage<{ failures: UpstreamFailure[] }>} */
37
- const storage = new AsyncLocalStorage();
38
-
39
- /**
40
- * @param {UpstreamFailure} failure
41
- * @returns {void}
42
- */
43
- export function reportUpstreamFailure(failure) {
44
- const store = storage.getStore();
45
- if (!store) return;
46
-
47
- // Aynı hatayı hem otomatik sarmalayıcı hem uygulamanın istemcisi
48
- // bildirebilir; aynı satırı iki kez loglamanın faydası yok.
49
- const duplicate = store.failures.some(
50
- (existing) => existing.status === failure.status && existing.path === failure.path,
51
- );
52
- if (!duplicate) store.failures.push(failure);
53
- }
54
-
55
- /**
56
- * Geçici sayılan durumlar: tekrar denemekle düzelebilenler. Bu liste
57
- * `render.js` ile paylaşılır — hangi hatanın önbelleği engellediği ve hangi
58
- * hatanın `notFound()`u 404 olmaktan çıkardığı tek yerde tanımlı olsun.
59
- */
60
- const TRANSIENT_STATUSES = new Set([0, 408, 425, 429]);
61
-
62
- /**
63
- * @param {number} status
64
- * @returns {boolean}
65
- */
66
- export function isTransientStatus(status) {
67
- return TRANSIENT_STATUSES.has(status) || status >= 500;
68
- }
69
-
70
- /**
71
- * `globalThis.fetch`i sarıp **geçici** upstream hatalarını kendiliğinden
72
- * bildirir.
73
- *
74
- * Gerekçesi pratik: `reportUpstreamFailure()` sözleşmesi uygulamanın HTTP
75
- * istemcisine bir satır eklemeyi gerektiriyor ve o satır yazılmadığında
76
- * framework rate limit'i hiç göremiyor — veri gelmediği için `notFound()`
77
- * çağıran sayfa 404 olarak servis ediliyordu. Otomatik izleme bu bilgiyi
78
- * varsayılan hâle getirir; elle çağrı hâlâ geçerli ve tekilleştirilir.
79
- *
80
- * Yalnızca geçici durumlar bildirilir. `404`/`403` gibi deterministik
81
- * cevaplar birçok API'de "böyle bir kayıt yok" anlamına geliyor ve onları
82
- * otomatik olarak "eksik veri" saymak her sayfada yanlış uyarı üretirdi.
83
- *
84
- * Kendi sunucumuza yapılan istekler atlanır: ısıtma turu ve sağlık kontrolü
85
- * upstream değil.
86
- *
87
- * @returns {void}
88
- */
89
- export function trackUpstreamFetch() {
90
- const original = globalThis.fetch;
91
- if (/** @type {any} */ (original).__jskeletUpstreamTracked) return;
92
-
93
- /** @type {typeof fetch} */
94
- const wrapped = async (input, init) => {
95
- // İstek bir render bağlamı içinde değilse (script, zamanlayıcı) hiçbir
96
- // şey yapılmaz: sarmalayıcının maliyeti bir `getStore()` çağrısı.
97
- if (!storage.getStore()) return original(input, init);
98
-
99
- const url = requestUrl(input);
100
- if (isSelfRequest(url)) return original(input, init);
101
-
102
- // Hız freni burada, çünkü kotayı harcayan şey sayfa değil bu çağrı.
103
- // Kapalıysa (varsayılan) `null` döner ve tek maliyeti bir dal.
104
- const permit = await limitUpstream(url);
105
-
106
- if (permit?.blocked) {
107
- // Devre kesici açık: 429 yiyeceğini bildiğimiz bir çağrıyı yapmıyoruz.
108
- // Render tarafı bunu geçici hata olarak görür, yani sayfa önbelleğe
109
- // yazılmaz ve bir sonraki istek yeniden dener.
110
- reportUpstreamFailure({ status: 429, path: url });
111
- return new Response(null, { status: 429, statusText: "Too Many Requests" });
112
- }
113
-
114
- try {
115
- const response = await original(input, init);
116
-
117
- if (permit) {
118
- noteUpstreamResponse(permit.host, response.status, response.headers.get("retry-after"));
119
- }
120
-
121
- if (!response.ok && isTransientStatus(response.status)) {
122
- reportUpstreamFailure({ status: response.status, path: url });
123
- }
124
- return response;
125
- } catch (error) {
126
- // Yanıt hiç gelmedi: ağ hatası her zaman geçicidir.
127
- if (permit) noteUpstreamResponse(permit.host, 0, null);
128
- reportUpstreamFailure({ status: 0, path: url });
129
- throw error;
130
- } finally {
131
- permit?.release();
132
- }
133
- };
134
-
135
- /** @type {any} */ (wrapped).__jskeletUpstreamTracked = true;
136
- globalThis.fetch = wrapped;
137
- }
138
-
139
- /**
140
- * @param {RequestInfo | URL} input
141
- * @returns {string}
142
- */
143
- function requestUrl(input) {
144
- if (typeof input === "string") return input;
145
- if (input instanceof URL) return input.href;
146
- return /** @type {Request} */ (input)?.url ?? String(input);
147
- }
148
-
149
- /** @param {string} url */
150
- function isSelfRequest(url) {
151
- return /^https?:\/\/(127\.0\.0\.1|\[::1\]|localhost)(:|\/|$)/i.test(url);
152
- }
153
-
154
- /**
155
- * @param {() => T} run
156
- * @returns {T}
157
- * @template T
158
- */
159
- export function withUpstreamTracking(run) {
160
- return storage.run({ failures: [] }, run);
161
- }
162
-
163
- /** @returns {UpstreamFailure[]} */
164
- export function getUpstreamFailures() {
165
- return storage.getStore()?.failures ?? [];
166
- }
1
+ /**
2
+ * Render başına upstream API hatalarını toplar.
3
+ *
4
+ * `render.js` her sayfayı bu bağlam içinde üretir. Böylece HTML önbelleği "bu
5
+ * çıktı eksik veriyle üretildi" bilgisine sahip olur ve bozuk sayfayı saklamaz;
6
+ * `notFound()` de geçici bir hataya denk geldiğinde 404 olmaktan çıkar.
7
+ *
8
+ * Bilgi iki yoldan gelir:
9
+ *
10
+ * 1. **Otomatik** — `trackUpstreamFetch()` `globalThis.fetch`i sarar ve
11
+ * geçici hataları (429, 5xx, ağ) kendiliğinden bildirir. `createApp()`
12
+ * bunu açılışta kurar, yani hiçbir uygulama kodu gerekmez.
13
+ * 2. **Elle** — `fetch` kullanmayan bir istemci (veritabanı sürücüsü, gRPC,
14
+ * SDK) için:
15
+ *
16
+ * import { reportUpstreamFailure } from "jskelet";
17
+ *
18
+ * if (!response.ok) {
19
+ * reportUpstreamFailure({ status: response.status, path: url });
20
+ * }
21
+ *
22
+ * İki yol aynı hatayı bildirirse tekilleştirilir.
23
+ *
24
+ * Sarmalayıcı aynı zamanda hız freninin durduğu yerdir
25
+ * (`upstream-limiter.js`): kotayı harcayan şey sayfa isteği değil, buradan
26
+ * geçen çağrı. Fren varsayılan olarak kapalı.
27
+ */
28
+ import { AsyncLocalStorage } from "node:async_hooks";
29
+ import { limitUpstream, noteUpstreamResponse } from "./upstream-limiter.js";
30
+
31
+ /**
32
+ * @typedef {{ status: number, path: string }} UpstreamFailure
33
+ * `status: 0` ağ hatası anlamına gelir (yanıt hiç gelmedi).
34
+ */
35
+
36
+ /** @type {AsyncLocalStorage<{ failures: UpstreamFailure[] }>} */
37
+ const storage = new AsyncLocalStorage();
38
+
39
+ /**
40
+ * @param {UpstreamFailure} failure
41
+ * @returns {void}
42
+ */
43
+ export function reportUpstreamFailure(failure) {
44
+ const store = storage.getStore();
45
+ if (!store) return;
46
+
47
+ // Aynı hatayı hem otomatik sarmalayıcı hem uygulamanın istemcisi
48
+ // bildirebilir; aynı satırı iki kez loglamanın faydası yok.
49
+ const duplicate = store.failures.some(
50
+ (existing) => existing.status === failure.status && existing.path === failure.path,
51
+ );
52
+ if (!duplicate) store.failures.push(failure);
53
+ }
54
+
55
+ /**
56
+ * Geçici sayılan durumlar: tekrar denemekle düzelebilenler. Bu liste
57
+ * `render.js` ile paylaşılır — hangi hatanın önbelleği engellediği ve hangi
58
+ * hatanın `notFound()`u 404 olmaktan çıkardığı tek yerde tanımlı olsun.
59
+ */
60
+ const TRANSIENT_STATUSES = new Set([0, 408, 425, 429]);
61
+
62
+ /**
63
+ * @param {number} status
64
+ * @returns {boolean}
65
+ */
66
+ export function isTransientStatus(status) {
67
+ return TRANSIENT_STATUSES.has(status) || status >= 500;
68
+ }
69
+
70
+ /**
71
+ * `globalThis.fetch`i sarıp **geçici** upstream hatalarını kendiliğinden
72
+ * bildirir.
73
+ *
74
+ * Gerekçesi pratik: `reportUpstreamFailure()` sözleşmesi uygulamanın HTTP
75
+ * istemcisine bir satır eklemeyi gerektiriyor ve o satır yazılmadığında
76
+ * framework rate limit'i hiç göremiyor — veri gelmediği için `notFound()`
77
+ * çağıran sayfa 404 olarak servis ediliyordu. Otomatik izleme bu bilgiyi
78
+ * varsayılan hâle getirir; elle çağrı hâlâ geçerli ve tekilleştirilir.
79
+ *
80
+ * Yalnızca geçici durumlar bildirilir. `404`/`403` gibi deterministik
81
+ * cevaplar birçok API'de "böyle bir kayıt yok" anlamına geliyor ve onları
82
+ * otomatik olarak "eksik veri" saymak her sayfada yanlış uyarı üretirdi.
83
+ *
84
+ * Kendi sunucumuza yapılan istekler atlanır: ısıtma turu ve sağlık kontrolü
85
+ * upstream değil.
86
+ *
87
+ * @returns {void}
88
+ */
89
+ export function trackUpstreamFetch() {
90
+ const original = globalThis.fetch;
91
+ if (/** @type {any} */ (original).__jskeletUpstreamTracked) return;
92
+
93
+ /** @type {typeof fetch} */
94
+ const wrapped = async (input, init) => {
95
+ // İstek bir render bağlamı içinde değilse (script, zamanlayıcı) hiçbir
96
+ // şey yapılmaz: sarmalayıcının maliyeti bir `getStore()` çağrısı.
97
+ if (!storage.getStore()) return original(input, init);
98
+
99
+ const url = requestUrl(input);
100
+ if (isSelfRequest(url)) return original(input, init);
101
+
102
+ // Hız freni burada, çünkü kotayı harcayan şey sayfa değil bu çağrı.
103
+ // Kapalıysa (varsayılan) `null` döner ve tek maliyeti bir dal.
104
+ const permit = await limitUpstream(url);
105
+
106
+ if (permit?.blocked) {
107
+ // Devre kesici açık: 429 yiyeceğini bildiğimiz bir çağrıyı yapmıyoruz.
108
+ // Render tarafı bunu geçici hata olarak görür, yani sayfa önbelleğe
109
+ // yazılmaz ve bir sonraki istek yeniden dener.
110
+ reportUpstreamFailure({ status: 429, path: url });
111
+ return new Response(null, { status: 429, statusText: "Too Many Requests" });
112
+ }
113
+
114
+ try {
115
+ const response = await original(input, init);
116
+
117
+ if (permit) {
118
+ noteUpstreamResponse(permit.host, response.status, response.headers.get("retry-after"));
119
+ }
120
+
121
+ if (!response.ok && isTransientStatus(response.status)) {
122
+ reportUpstreamFailure({ status: response.status, path: url });
123
+ }
124
+ return response;
125
+ } catch (error) {
126
+ // Yanıt hiç gelmedi: ağ hatası her zaman geçicidir.
127
+ if (permit) noteUpstreamResponse(permit.host, 0, null);
128
+ reportUpstreamFailure({ status: 0, path: url });
129
+ throw error;
130
+ } finally {
131
+ permit?.release();
132
+ }
133
+ };
134
+
135
+ /** @type {any} */ (wrapped).__jskeletUpstreamTracked = true;
136
+ globalThis.fetch = wrapped;
137
+ }
138
+
139
+ /**
140
+ * @param {RequestInfo | URL} input
141
+ * @returns {string}
142
+ */
143
+ function requestUrl(input) {
144
+ if (typeof input === "string") return input;
145
+ if (input instanceof URL) return input.href;
146
+ return /** @type {Request} */ (input)?.url ?? String(input);
147
+ }
148
+
149
+ /** @param {string} url */
150
+ function isSelfRequest(url) {
151
+ return /^https?:\/\/(127\.0\.0\.1|\[::1\]|localhost)(:|\/|$)/i.test(url);
152
+ }
153
+
154
+ /**
155
+ * @param {() => T} run
156
+ * @returns {T}
157
+ * @template T
158
+ */
159
+ export function withUpstreamTracking(run) {
160
+ return storage.run({ failures: [] }, run);
161
+ }
162
+
163
+ /** @returns {UpstreamFailure[]} */
164
+ export function getUpstreamFailures() {
165
+ return storage.getStore()?.failures ?? [];
166
+ }
package/src/start.mjs CHANGED
@@ -1,7 +1,7 @@
1
- /**
2
- * Prod sunucu girişi. Build çıktısı yoksa önce üretir, sonra dinlemeye başlar.
3
- */
4
- import "./build/ensure-build.mjs";
5
- import { startServer } from "./server/create-app.js";
6
-
7
- await startServer();
1
+ /**
2
+ * Prod sunucu girişi. Build çıktısı yoksa önce üretir, sonra dinlemeye başlar.
3
+ */
4
+ import "./build/ensure-build.mjs";
5
+ import { startServer } from "./server/create-app.js";
6
+
7
+ await startServer();
@@ -1,44 +1,44 @@
1
- <%#
2
- JSkelet varsayılan layout'u.
3
-
4
- Uygulama `views/layout.ejs` oluşturduğu anda bu dosya devre dışı kalır;
5
- amacı yeni bir projenin tek route ile çalışabilmesi. Kopyalayıp başlangıç
6
- noktası olarak kullanın.
7
-
8
- Kullanılabilir local'ler: metadata, headMeta, extraHead, structuredData,
9
- body, bodyClass, entries, pathname, lang, devtools, devBasePath, asset,
10
- hasAsset + tüm html/tag helper'ları ve views/components/** export'ları.
11
- `hooks.layoutContext()` döndürdüğü her alan da buraya eklenir.
12
- -%>
13
- <!DOCTYPE html>
14
- <html lang="<%= lang %>">
15
- <head>
16
- <meta charset="utf-8">
17
- <meta name="viewport" content="width=device-width, initial-scale=1">
18
- <%# Kaynak ipuçları en başta: preconnect ve LCP preload'ını geciktirmek
19
- doğrudan LCP'ye yazılır. Sayfaya özel head buradan gelir. %>
20
- <%- extraHead %>
21
- <%# Tek, render-blocking stylesheet — build çalışmadıysa hiç basılmaz. %>
22
- <% if (hasAsset('app.css')) { %>
23
- <link rel="stylesheet" href="<%= asset('app.css') %>">
24
- <% } %>
25
- <%- headMeta %>
26
- <% structuredData.forEach(function (item) { %>
27
- <script type="application/ld+json"><%- jsonScript(item) %></script>
28
- <% }); %>
29
- </head>
30
- <body class="<%= bodyClass %>">
31
- <%- body %>
32
- <%# Island runtime; entry yoksa sayfa yine tam çalışır (sadece JS'siz). %>
33
- <% if (hasAsset('main.js')) { %>
34
- <script type="module" src="<%= asset('main.js') %>"></script>
35
- <% } %>
36
- <% entries.forEach(function (entry) { %>
37
- <script type="module" src="<%= asset(entry) %>"></script>
38
- <% }); %>
39
- <%# Dev overlay: prod build'de yok, yalnızca `jskelet dev` sunucusundan. %>
40
- <% if (devtools) { %>
41
- <script type="module" src="<%= devBasePath %>/overlay.js"></script>
42
- <% } %>
43
- </body>
44
- </html>
1
+ <%#
2
+ JSkelet varsayılan layout'u.
3
+
4
+ Uygulama `views/layout.ejs` oluşturduğu anda bu dosya devre dışı kalır;
5
+ amacı yeni bir projenin tek route ile çalışabilmesi. Kopyalayıp başlangıç
6
+ noktası olarak kullanın.
7
+
8
+ Kullanılabilir local'ler: metadata, headMeta, extraHead, structuredData,
9
+ body, bodyClass, entries, pathname, lang, devtools, devBasePath, asset,
10
+ hasAsset + tüm html/tag helper'ları ve views/components/** export'ları.
11
+ `hooks.layoutContext()` döndürdüğü her alan da buraya eklenir.
12
+ -%>
13
+ <!DOCTYPE html>
14
+ <html lang="<%= lang %>">
15
+ <head>
16
+ <meta charset="utf-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1">
18
+ <%# Kaynak ipuçları en başta: preconnect ve LCP preload'ını geciktirmek
19
+ doğrudan LCP'ye yazılır. Sayfaya özel head buradan gelir. %>
20
+ <%- extraHead %>
21
+ <%# Tek, render-blocking stylesheet — build çalışmadıysa hiç basılmaz. %>
22
+ <% if (hasAsset('app.css')) { %>
23
+ <link rel="stylesheet" href="<%= asset('app.css') %>">
24
+ <% } %>
25
+ <%- headMeta %>
26
+ <% structuredData.forEach(function (item) { %>
27
+ <script type="application/ld+json"><%- jsonScript(item) %></script>
28
+ <% }); %>
29
+ </head>
30
+ <body class="<%= bodyClass %>">
31
+ <%- body %>
32
+ <%# Island runtime; entry yoksa sayfa yine tam çalışır (sadece JS'siz). %>
33
+ <% if (hasAsset('main.js')) { %>
34
+ <script type="module" src="<%= asset('main.js') %>"></script>
35
+ <% } %>
36
+ <% entries.forEach(function (entry) { %>
37
+ <script type="module" src="<%= asset(entry) %>"></script>
38
+ <% }); %>
39
+ <%# Dev overlay: prod build'de yok, yalnızca `jskelet dev` sunucusundan. %>
40
+ <% if (devtools) { %>
41
+ <script type="module" src="<%= devBasePath %>/overlay.js"></script>
42
+ <% } %>
43
+ </body>
44
+ </html>
package/src/version.mjs CHANGED
@@ -1,31 +1,31 @@
1
- /**
2
- * Framework sürümü. `package.json`'dan okunur; paket bir kez yayınlandıktan
3
- * sonra sürümü iki yerde tutmak kaçınılmaz olarak birbirinden ayrılıyor.
4
- */
5
- import fs from "node:fs";
6
- import path from "node:path";
7
- import { FRAMEWORK_ROOT } from "./config/index.js";
8
-
9
- const manifest = (() => {
10
- try {
11
- const file = path.join(FRAMEWORK_ROOT, "package.json");
12
- return JSON.parse(fs.readFileSync(file, "utf8"));
13
- } catch {
14
- return {};
15
- }
16
- })();
17
-
18
- /** @type {string} */
19
- export const FRAMEWORK_VERSION = manifest.version ?? "0.0.0";
20
-
21
- /** Paket adı: sürüm kontrolü hangi kayıt defteri girdisine bakacağını buradan bilir. */
22
- export const FRAMEWORK_PACKAGE = manifest.name ?? "jskelet";
23
-
24
- /** Künye alanları: yönetim panelinin footer'ı bunları basıyor. */
25
- export const FRAMEWORK_LICENSE = manifest.license ?? "MIT";
26
-
27
- /** @type {string} */
28
- export const FRAMEWORK_NODE_RANGE = manifest.engines?.node ?? "";
29
-
30
- /** @type {string} */
31
- export const FRAMEWORK_HOMEPAGE = manifest.homepage ?? "";
1
+ /**
2
+ * Framework sürümü. `package.json`'dan okunur; paket bir kez yayınlandıktan
3
+ * sonra sürümü iki yerde tutmak kaçınılmaz olarak birbirinden ayrılıyor.
4
+ */
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import { FRAMEWORK_ROOT } from "./config/index.js";
8
+
9
+ const manifest = (() => {
10
+ try {
11
+ const file = path.join(FRAMEWORK_ROOT, "package.json");
12
+ return JSON.parse(fs.readFileSync(file, "utf8"));
13
+ } catch {
14
+ return {};
15
+ }
16
+ })();
17
+
18
+ /** @type {string} */
19
+ export const FRAMEWORK_VERSION = manifest.version ?? "0.0.0";
20
+
21
+ /** Paket adı: sürüm kontrolü hangi kayıt defteri girdisine bakacağını buradan bilir. */
22
+ export const FRAMEWORK_PACKAGE = manifest.name ?? "jskelet";
23
+
24
+ /** Künye alanları: yönetim panelinin footer'ı bunları basıyor. */
25
+ export const FRAMEWORK_LICENSE = manifest.license ?? "MIT";
26
+
27
+ /** @type {string} */
28
+ export const FRAMEWORK_NODE_RANGE = manifest.engines?.node ?? "";
29
+
30
+ /** @type {string} */
31
+ export const FRAMEWORK_HOMEPAGE = manifest.homepage ?? "";