jskelet 0.6.2 → 0.6.3

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 (155) hide show
  1. package/AGENTS.md +136 -136
  2. package/CHANGELOG.md +620 -596
  3. package/LICENSE +21 -21
  4. package/bin/jskelet.mjs +130 -130
  5. package/docs/01-baslangic.md +291 -291
  6. package/docs/02-mimari.md +310 -309
  7. package/docs/03-routing.md +515 -515
  8. package/docs/04-render-ve-sablonlar.md +661 -661
  9. package/docs/05-islands.md +486 -486
  10. package/docs/06-cache.md +1443 -1423
  11. package/docs/07-yapilandirma.md +12 -6
  12. package/docs/08-build.md +429 -428
  13. package/docs/09-dev-araclari.md +364 -364
  14. package/docs/10-dagitim.md +338 -338
  15. package/docs/12-panel-ve-oturum.md +478 -478
  16. package/docs/README.md +83 -83
  17. package/docs/en/01-getting-started.md +298 -298
  18. package/docs/en/02-architecture.md +329 -328
  19. package/docs/en/03-routing.md +531 -531
  20. package/docs/en/04-rendering.md +669 -669
  21. package/docs/en/05-islands.md +497 -497
  22. package/docs/en/06-caching.md +1453 -1431
  23. package/docs/en/07-configuration.md +1219 -1214
  24. package/docs/en/08-build.md +447 -446
  25. package/docs/en/09-dev-tools.md +373 -373
  26. package/docs/en/10-deployment.md +340 -340
  27. package/docs/en/11-migration.md +398 -398
  28. package/docs/en/12-dashboards-and-sessions.md +488 -488
  29. package/docs/en/README.md +87 -87
  30. package/package.json +137 -137
  31. package/src/build/ensure-build.mjs +19 -19
  32. package/src/build/paths.mjs +153 -153
  33. package/src/build/resolve-peer.mjs +36 -36
  34. package/src/build/tasks/client.mjs +349 -349
  35. package/src/build/tasks/css.mjs +235 -235
  36. package/src/build/tasks/fonts.mjs +146 -146
  37. package/src/build/tasks/icons.mjs +357 -357
  38. package/src/build/tasks/images.mjs +244 -244
  39. package/src/build/tasks/precompress.mjs +78 -78
  40. package/src/build/tasks/templates.mjs +20 -20
  41. package/src/client/admin/i18n.js +764 -764
  42. package/src/client/admin/login.html +74 -74
  43. package/src/client/admin/panel.css +809 -809
  44. package/src/client/admin/panel.html +495 -495
  45. package/src/client/admin/panel.js +1251 -1251
  46. package/src/client/devtools/report.html +185 -185
  47. package/src/client/devtools/report.js +745 -745
  48. package/src/client/devtools/seo.js +628 -628
  49. package/src/client/dom.js +95 -95
  50. package/src/client/form.js +192 -192
  51. package/src/client/index.js +45 -45
  52. package/src/client/registry.js +305 -305
  53. package/src/client/safe-image.js +91 -91
  54. package/src/client/shared-cookie.js +225 -225
  55. package/src/client/store.js +36 -36
  56. package/src/client/swap.js +188 -188
  57. package/src/compile/codegen.js +336 -336
  58. package/src/compile/compile-all.js +149 -149
  59. package/src/compile/errors.js +66 -66
  60. package/src/compile/expr.js +409 -409
  61. package/src/compile/index.js +17 -17
  62. package/src/compile/parse.js +541 -541
  63. package/src/compile/resolve.js +211 -211
  64. package/src/compile/scan-exports.js +51 -51
  65. package/src/config/defaults.js +17 -1
  66. package/src/config/index.js +13 -0
  67. package/src/config/pattern.js +107 -107
  68. package/src/generate.mjs +163 -163
  69. package/src/http/control-flow.js +71 -71
  70. package/src/http/cookies-entry.js +21 -21
  71. package/src/http/cookies.js +277 -277
  72. package/src/http/request-cache.js +46 -46
  73. package/src/http/request-context.js +165 -165
  74. package/src/http/shared-cookie.js +178 -178
  75. package/src/index.js +101 -101
  76. package/src/init.mjs +230 -230
  77. package/src/migrate/apply.mjs +262 -262
  78. package/src/migrate/babel.mjs +79 -79
  79. package/src/migrate/classify.mjs +155 -155
  80. package/src/migrate/config.mjs +126 -126
  81. package/src/migrate/fs-walk.mjs +191 -191
  82. package/src/migrate/parse.mjs +26 -26
  83. package/src/migrate/scan.mjs +177 -177
  84. package/src/migrate/transform/expr-source.mjs +168 -168
  85. package/src/migrate/transform/island.mjs +67 -67
  86. package/src/migrate/transform/jsx-to-component.mjs +302 -302
  87. package/src/migrate/transform/jsx-to-jsk.mjs +330 -330
  88. package/src/migrate/transform/page-split.mjs +435 -435
  89. package/src/migrate/write.mjs +81 -81
  90. package/src/migrate.mjs +171 -171
  91. package/src/runtime/alias-hooks.mjs +119 -119
  92. package/src/runtime/register.mjs +4 -4
  93. package/src/server/admin/actions.js +229 -229
  94. package/src/server/admin/auth.js +125 -125
  95. package/src/server/admin/event-log.js +151 -151
  96. package/src/server/admin/gate.js +209 -209
  97. package/src/server/admin/inventory.js +188 -188
  98. package/src/server/admin/mount.js +56 -56
  99. package/src/server/admin/router.js +216 -216
  100. package/src/server/admin/snapshot.js +241 -241
  101. package/src/server/assets.js +147 -147
  102. package/src/server/auth/handoff.js +309 -309
  103. package/src/server/cache-blob.js +70 -0
  104. package/src/server/cache-deps.js +42 -42
  105. package/src/server/cache-vary.js +113 -113
  106. package/src/server/cloudflare.js +607 -607
  107. package/src/server/create-app.js +366 -366
  108. package/src/server/data-cache.js +553 -462
  109. package/src/server/dev/report.js +485 -485
  110. package/src/server/dev/socket.js +170 -170
  111. package/src/server/dev/version-check.mjs +139 -139
  112. package/src/server/disk-cache.js +233 -0
  113. package/src/server/ejs-adapter.js +59 -59
  114. package/src/server/html-cache.js +1196 -1122
  115. package/src/server/image-optimizer.js +500 -407
  116. package/src/server/logs/access-middleware.js +66 -66
  117. package/src/server/logs/file-sink.js +193 -66
  118. package/src/server/logs/pipeline.js +165 -158
  119. package/src/server/logs/s3-put.js +214 -214
  120. package/src/server/logs/s3-sink.js +112 -112
  121. package/src/server/metadata.js +102 -102
  122. package/src/server/middleware/compression.js +205 -205
  123. package/src/server/middleware/csrf.js +134 -134
  124. package/src/server/middleware/dev-gate.js +75 -75
  125. package/src/server/middleware/headers.js +37 -37
  126. package/src/server/middleware/redirects.js +32 -32
  127. package/src/server/middleware/robots-txt.js +341 -341
  128. package/src/server/middleware/static-precompressed.js +121 -100
  129. package/src/server/middleware/trailing-slash.js +53 -53
  130. package/src/server/middleware/upstream-proxy.js +141 -141
  131. package/src/server/og-image.js +356 -356
  132. package/src/server/port-guard.js +255 -255
  133. package/src/server/prewarm.js +1082 -1058
  134. package/src/server/redis.js +588 -569
  135. package/src/server/render.js +4 -4
  136. package/src/server/router.js +157 -157
  137. package/src/server/status-page.js +265 -265
  138. package/src/server/upstream-limiter.js +376 -376
  139. package/src/server/upstream-tracking.js +166 -166
  140. package/src/shared/cookie-domain.js +66 -66
  141. package/src/start.mjs +22 -22
  142. package/src/templates/layout.ejs +30 -30
  143. package/src/templates/layout.jsk +30 -30
  144. package/src/version.mjs +31 -31
  145. package/src/views/components/loader.js +101 -101
  146. package/src/views/helpers/html.js +102 -102
  147. package/src/views/helpers/tags.js +375 -375
  148. package/types/config/defaults.d.ts +15 -1
  149. package/types/config/index.d.ts +8 -0
  150. package/types/server/cache-blob.d.ts +13 -0
  151. package/types/server/data-cache.d.ts +9 -0
  152. package/types/server/disk-cache.d.ts +36 -0
  153. package/types/server/html-cache.d.ts +26 -3
  154. package/types/server/logs/file-sink.d.ts +16 -5
  155. package/types/server/redis.d.ts +2 -1
@@ -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
+ }
@@ -1,66 +1,66 @@
1
- /**
2
- * Paylaşımlı cookie Domain seçimi — sunucu ve istemci aynı kuralları kullanır.
3
- *
4
- * `brand.sharedCookieRoots` örn. `[".investvio.com", ".localhost"]`.
5
- * Host bu köklerden birine uyuyorsa Domain olarak o kök yazılır.
6
- */
7
-
8
- /**
9
- * @param {string} host Host veya Host:port.
10
- * @returns {string} Lowercase, portsuz.
11
- */
12
- export function stripHostPort(host) {
13
- const raw = String(host ?? "")
14
- .split(",")[0]
15
- .trim()
16
- .toLowerCase();
17
- if (!raw) return "";
18
- if (raw.startsWith("[")) {
19
- const end = raw.indexOf("]");
20
- return end === -1 ? raw : raw.slice(0, end + 1);
21
- }
22
- const colon = raw.lastIndexOf(":");
23
- if (colon === -1) return raw;
24
- if (raw.indexOf(":") !== colon) return raw;
25
- return raw.slice(0, colon);
26
- }
27
-
28
- /**
29
- * Kökü `Domain=` biçimine getirir (başında nokta).
30
- *
31
- * @param {unknown} root
32
- * @returns {string | null}
33
- */
34
- export function normalizeCookieRoot(root) {
35
- if (typeof root !== "string") return null;
36
- const trimmed = root.trim().toLowerCase();
37
- if (!trimmed || trimmed === ".") return null;
38
- const bare = trimmed.replace(/^\./, "");
39
- if (!bare || bare.includes("/") || bare.includes(":")) return null;
40
- return `.${bare}`;
41
- }
42
-
43
- /**
44
- * @param {string} hostname Portsuz hostname.
45
- * @param {Iterable<string>} roots `brand.sharedCookieRoots`.
46
- * @returns {string | null} `Domain` değeri (başında nokta) ya da eşleşme yoksa null.
47
- */
48
- export function resolveSharedCookieDomain(hostname, roots) {
49
- const host = stripHostPort(hostname);
50
- if (!host) return null;
51
-
52
- for (const entry of roots ?? []) {
53
- const root = normalizeCookieRoot(entry);
54
- if (!root) continue;
55
- const bare = root.slice(1);
56
- if (host === bare || host.endsWith(`.${bare}`)) return root;
57
- }
58
-
59
- return null;
60
- }
61
-
62
- /**
63
- * Paylaşımlı cookie için önerilen üst sınır. JWT / büyük token buraya
64
- * sığmaz — kısa session id koyun; aksi halde handoff veya host-only cookie.
65
- */
66
- export const SHARED_COOKIE_WARN_BYTES = 512;
1
+ /**
2
+ * Paylaşımlı cookie Domain seçimi — sunucu ve istemci aynı kuralları kullanır.
3
+ *
4
+ * `brand.sharedCookieRoots` örn. `[".investvio.com", ".localhost"]`.
5
+ * Host bu köklerden birine uyuyorsa Domain olarak o kök yazılır.
6
+ */
7
+
8
+ /**
9
+ * @param {string} host Host veya Host:port.
10
+ * @returns {string} Lowercase, portsuz.
11
+ */
12
+ export function stripHostPort(host) {
13
+ const raw = String(host ?? "")
14
+ .split(",")[0]
15
+ .trim()
16
+ .toLowerCase();
17
+ if (!raw) return "";
18
+ if (raw.startsWith("[")) {
19
+ const end = raw.indexOf("]");
20
+ return end === -1 ? raw : raw.slice(0, end + 1);
21
+ }
22
+ const colon = raw.lastIndexOf(":");
23
+ if (colon === -1) return raw;
24
+ if (raw.indexOf(":") !== colon) return raw;
25
+ return raw.slice(0, colon);
26
+ }
27
+
28
+ /**
29
+ * Kökü `Domain=` biçimine getirir (başında nokta).
30
+ *
31
+ * @param {unknown} root
32
+ * @returns {string | null}
33
+ */
34
+ export function normalizeCookieRoot(root) {
35
+ if (typeof root !== "string") return null;
36
+ const trimmed = root.trim().toLowerCase();
37
+ if (!trimmed || trimmed === ".") return null;
38
+ const bare = trimmed.replace(/^\./, "");
39
+ if (!bare || bare.includes("/") || bare.includes(":")) return null;
40
+ return `.${bare}`;
41
+ }
42
+
43
+ /**
44
+ * @param {string} hostname Portsuz hostname.
45
+ * @param {Iterable<string>} roots `brand.sharedCookieRoots`.
46
+ * @returns {string | null} `Domain` değeri (başında nokta) ya da eşleşme yoksa null.
47
+ */
48
+ export function resolveSharedCookieDomain(hostname, roots) {
49
+ const host = stripHostPort(hostname);
50
+ if (!host) return null;
51
+
52
+ for (const entry of roots ?? []) {
53
+ const root = normalizeCookieRoot(entry);
54
+ if (!root) continue;
55
+ const bare = root.slice(1);
56
+ if (host === bare || host.endsWith(`.${bare}`)) return root;
57
+ }
58
+
59
+ return null;
60
+ }
61
+
62
+ /**
63
+ * Paylaşımlı cookie için önerilen üst sınır. JWT / büyük token buraya
64
+ * sığmaz — kısa session id koyun; aksi halde handoff veya host-only cookie.
65
+ */
66
+ export const SHARED_COOKIE_WARN_BYTES = 512;
package/src/start.mjs CHANGED
@@ -1,22 +1,22 @@
1
- /**
2
- * Prod sunucu girişi. Build çıktısı yoksa önce üretir, sonra dinlemeye başlar.
3
- *
4
- * `jskelet start --murder` → dolu porttaki dinleyiciyi öldürüp başlar.
5
- * Port kontrolü build'den önce: doluysa pahalı üretimi hiç başlatma.
6
- */
7
- import process from "node:process";
8
- import { ensurePortFree } from "./server/port-guard.js";
9
-
10
- const port = Number(process.env.PORT ?? 3000);
11
- const murder = process.argv.includes("--murder");
12
-
13
- try {
14
- await ensurePortFree(port, { murder });
15
- await import("./build/ensure-build.mjs");
16
- const { startServer } = await import("./server/create-app.js");
17
- await startServer({ murder, port });
18
- } catch (error) {
19
- const message = error instanceof Error ? error.message : String(error);
20
- process.stderr.write(`${message}\n`);
21
- process.exit(1);
22
- }
1
+ /**
2
+ * Prod sunucu girişi. Build çıktısı yoksa önce üretir, sonra dinlemeye başlar.
3
+ *
4
+ * `jskelet start --murder` → dolu porttaki dinleyiciyi öldürüp başlar.
5
+ * Port kontrolü build'den önce: doluysa pahalı üretimi hiç başlatma.
6
+ */
7
+ import process from "node:process";
8
+ import { ensurePortFree } from "./server/port-guard.js";
9
+
10
+ const port = Number(process.env.PORT ?? 3000);
11
+ const murder = process.argv.includes("--murder");
12
+
13
+ try {
14
+ await ensurePortFree(port, { murder });
15
+ await import("./build/ensure-build.mjs");
16
+ const { startServer } = await import("./server/create-app.js");
17
+ await startServer({ murder, port });
18
+ } catch (error) {
19
+ const message = error instanceof Error ? error.message : String(error);
20
+ process.stderr.write(`${message}\n`);
21
+ process.exit(1);
22
+ }
@@ -1,30 +1,30 @@
1
- <%#
2
- JSkelet varsayılan layout'unun legacy EJS kopyası.
3
-
4
- Yeni uygulamalar `jskelet/layout` (.jsk) kullanır. Bu dosya yalnızca EJS
5
- peer'i kurulu eski şablonlar için `jskelet/layout/ejs` üzerinden erişilir.
6
- Uygulama `views/layout.jsk` veya `views/layout.ejs` oluşturduğu anda
7
- framework layout'u devre dışı kalır.
8
-
9
- Kullanılabilir local'ler: metadata, headMeta, extraHead, structuredData,
10
- body, bodyClass, entries, styles, pathname, lang, devtools, devBasePath,
11
- asset, hasAsset + tüm html/tag helper'ları ve views/components/** export'ları.
12
- `hooks.layoutContext()` döndürdüğü her alan da buraya eklenir.
13
- -%>
14
- <!DOCTYPE html>
15
- <html lang="<%= lang %>">
16
- <head>
17
- <meta charset="utf-8">
18
- <meta name="viewport" content="width=device-width, initial-scale=1">
19
- <%# Kaynak ipuçları en başta: preconnect ve LCP preload'ını geciktirmek
20
- doğrudan LCP'ye yazılır. Sayfaya özel head buradan gelir. %>
21
- <%- extraHead %>
22
- <%- stylesheets({ styles }) %>
23
- <%- headMeta %>
24
- <%- jsonLd({ items: structuredData }) %>
25
- </head>
26
- <body class="<%= bodyClass %>">
27
- <%- body %>
28
- <%- bodyScripts({ entries, devtools, devBasePath }) %>
29
- </body>
30
- </html>
1
+ <%#
2
+ JSkelet varsayılan layout'unun legacy EJS kopyası.
3
+
4
+ Yeni uygulamalar `jskelet/layout` (.jsk) kullanır. Bu dosya yalnızca EJS
5
+ peer'i kurulu eski şablonlar için `jskelet/layout/ejs` üzerinden erişilir.
6
+ Uygulama `views/layout.jsk` veya `views/layout.ejs` oluşturduğu anda
7
+ framework layout'u devre dışı kalır.
8
+
9
+ Kullanılabilir local'ler: metadata, headMeta, extraHead, structuredData,
10
+ body, bodyClass, entries, styles, pathname, lang, devtools, devBasePath,
11
+ asset, hasAsset + tüm html/tag helper'ları ve views/components/** export'ları.
12
+ `hooks.layoutContext()` döndürdüğü her alan da buraya eklenir.
13
+ -%>
14
+ <!DOCTYPE html>
15
+ <html lang="<%= lang %>">
16
+ <head>
17
+ <meta charset="utf-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1">
19
+ <%# Kaynak ipuçları en başta: preconnect ve LCP preload'ını geciktirmek
20
+ doğrudan LCP'ye yazılır. Sayfaya özel head buradan gelir. %>
21
+ <%- extraHead %>
22
+ <%- stylesheets({ styles }) %>
23
+ <%- headMeta %>
24
+ <%- jsonLd({ items: structuredData }) %>
25
+ </head>
26
+ <body class="<%= bodyClass %>">
27
+ <%- body %>
28
+ <%- bodyScripts({ entries, devtools, devBasePath }) %>
29
+ </body>
30
+ </html>
@@ -1,30 +1,30 @@
1
- {#
2
- JSkelet varsayılan layout'u.
3
-
4
- Uygulama `views/layout.jsk` (veya legacy `views/layout.ejs`) oluşturduğu
5
- anda bu dosya devre dışı kalır; amacı yeni bir projenin tek route ile
6
- çalışabilmesi. Kopyalayıp başlangıç noktası olarak kullanın.
7
-
8
- Kullanılabilir local'ler: metadata, headMeta, extraHead, structuredData,
9
- body, bodyClass, entries, styles, pathname, lang, devtools, devBasePath,
10
- asset, 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
- Stylesheets / JsonLd / BodyScripts: `.jsk` ifade dilinde fonksiyon çağrısı
14
- olmadığı için asset döngüleri bu yerleşik etiketlere taşındı.
15
- #}
16
- <!DOCTYPE html>
17
- <html :lang="lang">
18
- <head>
19
- <meta charset="utf-8">
20
- <meta name="viewport" content="width=device-width, initial-scale=1">
21
- {{{ extraHead }}}
22
- <Stylesheets :styles="styles" />
23
- {{{ headMeta }}}
24
- <JsonLd :items="structuredData" />
25
- </head>
26
- <body :class="bodyClass">
27
- {{{ body }}}
28
- <BodyScripts :entries="entries" :devtools="devtools" :devBasePath="devBasePath" />
29
- </body>
30
- </html>
1
+ {#
2
+ JSkelet varsayılan layout'u.
3
+
4
+ Uygulama `views/layout.jsk` (veya legacy `views/layout.ejs`) oluşturduğu
5
+ anda bu dosya devre dışı kalır; amacı yeni bir projenin tek route ile
6
+ çalışabilmesi. Kopyalayıp başlangıç noktası olarak kullanın.
7
+
8
+ Kullanılabilir local'ler: metadata, headMeta, extraHead, structuredData,
9
+ body, bodyClass, entries, styles, pathname, lang, devtools, devBasePath,
10
+ asset, 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
+ Stylesheets / JsonLd / BodyScripts: `.jsk` ifade dilinde fonksiyon çağrısı
14
+ olmadığı için asset döngüleri bu yerleşik etiketlere taşındı.
15
+ #}
16
+ <!DOCTYPE html>
17
+ <html :lang="lang">
18
+ <head>
19
+ <meta charset="utf-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1">
21
+ {{{ extraHead }}}
22
+ <Stylesheets :styles="styles" />
23
+ {{{ headMeta }}}
24
+ <JsonLd :items="structuredData" />
25
+ </head>
26
+ <body :class="bodyClass">
27
+ {{{ body }}}
28
+ <BodyScripts :entries="entries" :devtools="devtools" :devBasePath="devBasePath" />
29
+ </body>
30
+ </html>