jskelet 0.6.3 → 0.6.4

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 (153) hide show
  1. package/AGENTS.md +136 -136
  2. package/CHANGELOG.md +628 -620
  3. package/LICENSE +21 -21
  4. package/README.md +2 -0
  5. package/bin/jskelet.mjs +130 -130
  6. package/docs/01-baslangic.md +291 -291
  7. package/docs/02-mimari.md +310 -310
  8. package/docs/03-routing.md +515 -515
  9. package/docs/04-render-ve-sablonlar.md +667 -661
  10. package/docs/05-islands.md +486 -486
  11. package/docs/06-cache.md +1467 -1443
  12. package/docs/07-yapilandirma.md +1208 -1197
  13. package/docs/08-build.md +429 -429
  14. package/docs/09-dev-araclari.md +364 -364
  15. package/docs/10-dagitim.md +348 -338
  16. package/docs/12-panel-ve-oturum.md +479 -478
  17. package/docs/README.md +83 -83
  18. package/docs/en/01-getting-started.md +298 -298
  19. package/docs/en/02-architecture.md +329 -329
  20. package/docs/en/03-routing.md +531 -531
  21. package/docs/en/04-rendering.md +675 -669
  22. package/docs/en/05-islands.md +497 -497
  23. package/docs/en/06-caching.md +1476 -1453
  24. package/docs/en/07-configuration.md +1229 -1219
  25. package/docs/en/08-build.md +447 -447
  26. package/docs/en/09-dev-tools.md +373 -373
  27. package/docs/en/10-deployment.md +351 -340
  28. package/docs/en/11-migration.md +398 -398
  29. package/docs/en/12-dashboards-and-sessions.md +489 -488
  30. package/docs/en/README.md +87 -87
  31. package/package.json +137 -137
  32. package/src/build/ensure-build.mjs +19 -19
  33. package/src/build/paths.mjs +153 -153
  34. package/src/build/resolve-peer.mjs +36 -36
  35. package/src/build/tasks/client.mjs +349 -349
  36. package/src/build/tasks/css.mjs +235 -235
  37. package/src/build/tasks/fonts.mjs +146 -146
  38. package/src/build/tasks/icons.mjs +357 -357
  39. package/src/build/tasks/images.mjs +244 -244
  40. package/src/build/tasks/precompress.mjs +78 -78
  41. package/src/build/tasks/templates.mjs +20 -20
  42. package/src/client/admin/i18n.js +764 -764
  43. package/src/client/admin/login.html +74 -74
  44. package/src/client/admin/panel.css +809 -809
  45. package/src/client/admin/panel.html +495 -495
  46. package/src/client/admin/panel.js +1251 -1251
  47. package/src/client/devtools/report.html +185 -185
  48. package/src/client/devtools/report.js +745 -745
  49. package/src/client/devtools/seo.js +628 -628
  50. package/src/client/dom.js +95 -95
  51. package/src/client/form.js +192 -192
  52. package/src/client/index.js +45 -45
  53. package/src/client/registry.js +305 -305
  54. package/src/client/safe-image.js +91 -91
  55. package/src/client/shared-cookie.js +225 -225
  56. package/src/client/store.js +36 -36
  57. package/src/client/swap.js +188 -188
  58. package/src/compile/codegen.js +336 -336
  59. package/src/compile/compile-all.js +149 -149
  60. package/src/compile/errors.js +66 -66
  61. package/src/compile/expr.js +409 -409
  62. package/src/compile/index.js +17 -17
  63. package/src/compile/parse.js +541 -541
  64. package/src/compile/resolve.js +211 -211
  65. package/src/compile/scan-exports.js +51 -51
  66. package/src/config/defaults.js +541 -534
  67. package/src/config/index.js +1500 -1469
  68. package/src/config/pattern.js +107 -107
  69. package/src/generate.mjs +163 -163
  70. package/src/http/control-flow.js +71 -71
  71. package/src/http/cookies-entry.js +21 -21
  72. package/src/http/cookies.js +277 -277
  73. package/src/http/request-cache.js +46 -46
  74. package/src/http/request-context.js +165 -165
  75. package/src/http/shared-cookie.js +178 -178
  76. package/src/index.js +101 -101
  77. package/src/init.mjs +232 -230
  78. package/src/migrate/apply.mjs +262 -262
  79. package/src/migrate/babel.mjs +79 -79
  80. package/src/migrate/classify.mjs +155 -155
  81. package/src/migrate/config.mjs +126 -126
  82. package/src/migrate/fs-walk.mjs +191 -191
  83. package/src/migrate/parse.mjs +26 -26
  84. package/src/migrate/scan.mjs +177 -177
  85. package/src/migrate/transform/expr-source.mjs +168 -168
  86. package/src/migrate/transform/island.mjs +67 -67
  87. package/src/migrate/transform/jsx-to-component.mjs +302 -302
  88. package/src/migrate/transform/jsx-to-jsk.mjs +330 -330
  89. package/src/migrate/transform/page-split.mjs +435 -435
  90. package/src/migrate/write.mjs +81 -81
  91. package/src/migrate.mjs +171 -171
  92. package/src/runtime/alias-hooks.mjs +119 -119
  93. package/src/runtime/register.mjs +4 -4
  94. package/src/server/admin/actions.js +229 -229
  95. package/src/server/admin/auth.js +125 -125
  96. package/src/server/admin/event-log.js +151 -151
  97. package/src/server/admin/gate.js +209 -209
  98. package/src/server/admin/inventory.js +188 -188
  99. package/src/server/admin/mount.js +56 -56
  100. package/src/server/admin/router.js +216 -216
  101. package/src/server/admin/snapshot.js +241 -241
  102. package/src/server/assets.js +147 -147
  103. package/src/server/auth/handoff.js +309 -309
  104. package/src/server/cache-blob.js +70 -70
  105. package/src/server/cache-control.js +45 -0
  106. package/src/server/cache-deps.js +42 -42
  107. package/src/server/cache-vary.js +113 -113
  108. package/src/server/cloudflare.js +607 -607
  109. package/src/server/create-app.js +366 -366
  110. package/src/server/data-cache.js +553 -553
  111. package/src/server/dev/report.js +485 -485
  112. package/src/server/dev/socket.js +170 -170
  113. package/src/server/dev/version-check.mjs +139 -139
  114. package/src/server/disk-cache.js +233 -233
  115. package/src/server/ejs-adapter.js +59 -59
  116. package/src/server/html-cache.js +1196 -1196
  117. package/src/server/image-optimizer.js +500 -500
  118. package/src/server/logs/access-middleware.js +66 -66
  119. package/src/server/logs/file-sink.js +193 -193
  120. package/src/server/logs/pipeline.js +165 -165
  121. package/src/server/logs/s3-put.js +214 -214
  122. package/src/server/logs/s3-sink.js +112 -112
  123. package/src/server/metadata.js +102 -102
  124. package/src/server/middleware/compression.js +205 -205
  125. package/src/server/middleware/csrf.js +134 -134
  126. package/src/server/middleware/dev-gate.js +75 -75
  127. package/src/server/middleware/headers.js +37 -37
  128. package/src/server/middleware/redirects.js +32 -32
  129. package/src/server/middleware/robots-txt.js +341 -341
  130. package/src/server/middleware/static-precompressed.js +121 -121
  131. package/src/server/middleware/trailing-slash.js +53 -53
  132. package/src/server/middleware/upstream-proxy.js +141 -141
  133. package/src/server/og-image.js +369 -356
  134. package/src/server/port-guard.js +255 -255
  135. package/src/server/prewarm.js +1082 -1082
  136. package/src/server/redis.js +588 -588
  137. package/src/server/render.js +910 -910
  138. package/src/server/router.js +157 -157
  139. package/src/server/status-page.js +265 -265
  140. package/src/server/upstream-limiter.js +376 -376
  141. package/src/server/upstream-tracking.js +166 -166
  142. package/src/shared/cookie-domain.js +66 -66
  143. package/src/start.mjs +22 -22
  144. package/src/templates/layout.ejs +30 -30
  145. package/src/templates/layout.jsk +30 -30
  146. package/src/version.mjs +31 -31
  147. package/src/views/components/loader.js +101 -101
  148. package/src/views/helpers/html.js +102 -102
  149. package/src/views/helpers/tags.js +375 -375
  150. package/types/config/defaults.d.ts +6 -0
  151. package/types/config/index.d.ts +6 -0
  152. package/types/server/cache-control.d.ts +28 -0
  153. package/types/server/og-image.d.ts +5 -0
@@ -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>