jskelet 0.2.3 → 0.2.5

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 (91) hide show
  1. package/AGENTS.md +132 -132
  2. package/CHANGELOG.md +15 -0
  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 +287 -287
  7. package/docs/03-routing.md +480 -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 +1209 -1202
  11. package/docs/08-build.md +366 -366
  12. package/docs/09-dev-araclari.md +335 -335
  13. package/docs/10-dagitim.md +329 -329
  14. package/docs/12-panel-ve-oturum.md +384 -384
  15. package/docs/README.md +105 -105
  16. package/docs/en/01-getting-started.md +292 -292
  17. package/docs/en/02-architecture.md +305 -305
  18. package/docs/en/03-routing.md +497 -497
  19. package/docs/en/04-rendering.md +504 -504
  20. package/docs/en/05-islands.md +492 -492
  21. package/docs/en/06-caching.md +1239 -1232
  22. package/docs/en/07-configuration.md +986 -986
  23. package/docs/en/08-build.md +383 -383
  24. package/docs/en/09-dev-tools.md +342 -342
  25. package/docs/en/10-deployment.md +332 -332
  26. package/docs/en/11-migration.md +359 -359
  27. package/docs/en/12-dashboards-and-sessions.md +392 -392
  28. package/docs/en/README.md +112 -112
  29. package/package.json +102 -102
  30. package/src/build/ensure-build.mjs +15 -15
  31. package/src/build/paths.mjs +143 -143
  32. package/src/build/resolve-peer.mjs +36 -36
  33. package/src/build/tasks/client.mjs +268 -268
  34. package/src/build/tasks/css.mjs +124 -124
  35. package/src/build/tasks/fonts.mjs +146 -146
  36. package/src/build/tasks/icons.mjs +224 -224
  37. package/src/build/tasks/images.mjs +244 -244
  38. package/src/build/tasks/precompress.mjs +78 -78
  39. package/src/client/cache-panel/i18n.js +670 -0
  40. package/src/client/cache-panel/login.html +74 -71
  41. package/src/client/cache-panel/panel.css +756 -740
  42. package/src/client/cache-panel/panel.html +308 -307
  43. package/src/client/cache-panel/panel.js +915 -808
  44. package/src/client/devtools/report.html +185 -185
  45. package/src/client/devtools/report.js +725 -725
  46. package/src/client/dom.js +95 -95
  47. package/src/client/form.js +192 -192
  48. package/src/client/index.js +35 -35
  49. package/src/client/registry.js +297 -297
  50. package/src/client/safe-image.js +91 -91
  51. package/src/client/store.js +36 -36
  52. package/src/client/swap.js +188 -188
  53. package/src/config/pattern.js +107 -107
  54. package/src/http/control-flow.js +71 -71
  55. package/src/http/cookies.js +257 -257
  56. package/src/http/request-cache.js +46 -46
  57. package/src/http/request-context.js +162 -162
  58. package/src/index.js +83 -83
  59. package/src/init.mjs +221 -221
  60. package/src/runtime/alias-hooks.mjs +119 -119
  61. package/src/runtime/register.mjs +4 -4
  62. package/src/server/assets.js +147 -147
  63. package/src/server/cache-deps.js +42 -42
  64. package/src/server/cache-panel.js +759 -738
  65. package/src/server/cloudflare.js +607 -595
  66. package/src/server/create-app.js +291 -291
  67. package/src/server/data-cache.js +462 -462
  68. package/src/server/dev/report.js +369 -369
  69. package/src/server/dev/socket.js +170 -170
  70. package/src/server/dev/version-check.mjs +139 -139
  71. package/src/server/html-cache.js +817 -817
  72. package/src/server/metadata.js +102 -102
  73. package/src/server/middleware/compression.js +205 -205
  74. package/src/server/middleware/csrf.js +134 -134
  75. package/src/server/middleware/dev-gate.js +62 -62
  76. package/src/server/middleware/headers.js +37 -37
  77. package/src/server/middleware/redirects.js +32 -32
  78. package/src/server/middleware/static-precompressed.js +100 -100
  79. package/src/server/middleware/upstream-proxy.js +141 -141
  80. package/src/server/prewarm.js +601 -601
  81. package/src/server/redis.js +569 -569
  82. package/src/server/router.js +128 -128
  83. package/src/server/status-page.js +164 -164
  84. package/src/server/upstream-limiter.js +376 -376
  85. package/src/server/upstream-tracking.js +166 -166
  86. package/src/start.mjs +7 -7
  87. package/src/templates/layout.ejs +44 -44
  88. package/src/version.mjs +31 -31
  89. package/src/views/components/loader.js +85 -85
  90. package/src/views/helpers/html.js +102 -102
  91. package/src/views/helpers/tags.js +245 -245
@@ -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 ?? "";