jskelet 0.6.2 → 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.
- package/CHANGELOG.md +32 -0
- package/README.md +2 -0
- package/docs/02-mimari.md +1 -0
- package/docs/04-render-ve-sablonlar.md +9 -3
- package/docs/06-cache.md +53 -9
- package/docs/07-yapilandirma.md +1208 -1191
- package/docs/08-build.md +2 -1
- package/docs/10-dagitim.md +16 -6
- package/docs/12-panel-ve-oturum.md +2 -1
- package/docs/en/02-architecture.md +2 -1
- package/docs/en/04-rendering.md +9 -3
- package/docs/en/06-caching.md +54 -9
- package/docs/en/07-configuration.md +24 -9
- package/docs/en/08-build.md +2 -1
- package/docs/en/10-deployment.md +17 -6
- package/docs/en/12-dashboards-and-sessions.md +2 -1
- package/package.json +1 -1
- package/src/config/defaults.js +541 -518
- package/src/config/index.js +1500 -1456
- package/src/init.mjs +2 -0
- package/src/server/cache-blob.js +70 -0
- package/src/server/cache-control.js +45 -0
- package/src/server/data-cache.js +118 -27
- package/src/server/disk-cache.js +233 -0
- package/src/server/html-cache.js +90 -16
- package/src/server/image-optimizer.js +95 -2
- package/src/server/logs/file-sink.js +159 -32
- package/src/server/logs/pipeline.js +10 -3
- package/src/server/middleware/static-precompressed.js +31 -10
- package/src/server/og-image.js +17 -4
- package/src/server/prewarm.js +25 -1
- package/src/server/redis.js +31 -12
- package/src/server/render.js +910 -910
- package/types/config/defaults.d.ts +21 -1
- package/types/config/index.d.ts +14 -0
- package/types/server/cache-blob.d.ts +13 -0
- package/types/server/cache-control.d.ts +28 -0
- package/types/server/data-cache.d.ts +9 -0
- package/types/server/disk-cache.d.ts +36 -0
- package/types/server/html-cache.d.ts +26 -3
- package/types/server/logs/file-sink.d.ts +16 -5
- package/types/server/og-image.d.ts +5 -0
- package/types/server/redis.d.ts +2 -1
package/docs/07-yapilandirma.md
CHANGED
|
@@ -1,1191 +1,1208 @@
|
|
|
1
|
-
# 07 — Yapılandırma referansı
|
|
2
|
-
|
|
3
|
-
Bu belge `jskelet.config.mjs`'in tam referansıdır: her alan, tipi, varsayılanı
|
|
4
|
-
ve örneği. Ardından `source` desen sözdizimi ve framework'ün okuduğu tüm ortam
|
|
5
|
-
değişkenleri tablosu geliyor. Alanların davranışsal ayrıntıları için ilgili
|
|
6
|
-
belgelere bağlantı verildi; buradaki amaç tek bakışta tam liste sunmak.
|
|
7
|
-
|
|
8
|
-
## Dosyanın konumu ve yüklenmesi
|
|
9
|
-
|
|
10
|
-
Config dosyası proje kökünde `jskelet.config.mjs` adıyla aranır ve **zorunlu
|
|
11
|
-
değildir**. Yoksa ya da okunamıyorsa uyarı basılır ve sunucu varsayılanlarla
|
|
12
|
-
ayağa kalkar; bozuk bir düzenleme siteyi açılamaz hâle getirmemeli.
|
|
13
|
-
|
|
14
|
-
```js
|
|
15
|
-
// jskelet.config.mjs
|
|
16
|
-
export default {
|
|
17
|
-
// …
|
|
18
|
-
};
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Default export yoksa modülün kendisi config olarak kullanılır (named export'lar).
|
|
22
|
-
|
|
23
|
-
`headers()`, `redirects()`, `rewrites()` ve `cache()` bölümleri fonksiyon **ya da
|
|
24
|
-
düz değer** olabilir; fonksiyon olmaları hâlinde `async` olabilirler ve `this`
|
|
25
|
-
config nesnesine bağlıdır. Bir bölüm hata verirse yalnızca o bölüm yok sayılır.
|
|
26
|
-
|
|
27
|
-
Config başarıyla yüklendiğinde bir özet basılır:
|
|
28
|
-
`[config] jskelet.config.mjs loaded — 3 headers, 2 redirects, 1 cache rule`
|
|
29
|
-
|
|
30
|
-
## Tam örnek
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
// jskelet.config.mjs
|
|
34
|
-
export default {
|
|
35
|
-
paths: {
|
|
36
|
-
views: "views",
|
|
37
|
-
public: "public",
|
|
38
|
-
client: "client",
|
|
39
|
-
routes: "routes",
|
|
40
|
-
styles: "styles/globals.css",
|
|
41
|
-
generated: ".jskelet",
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
brand: {
|
|
45
|
-
name: "Örnek",
|
|
46
|
-
poweredBy: "Örnek",
|
|
47
|
-
cacheHeader: "X-Ornek-Cache",
|
|
48
|
-
devBasePath: "/__ornek/dev",
|
|
49
|
-
prewarmUserAgent: "ornek-prewarm",
|
|
50
|
-
devTokenCookie: "dev_token",
|
|
51
|
-
lang: "tr",
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
layout: "views/layout.jsk",
|
|
55
|
-
routes: ["./routes/10-pages.mjs", "./routes/99-catch-all.mjs"],
|
|
56
|
-
trailingSlash: false,
|
|
57
|
-
|
|
58
|
-
static: {
|
|
59
|
-
extensions: [".svg", ".png", ".webp", ".avif", ".ico", ".woff2"],
|
|
60
|
-
prefixes: ["/assets/", "/fonts/"],
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
devGateBypass: ["/api/healthcheck", "/robots.txt"],
|
|
64
|
-
preconnect: ["https://cdn.ornek.com"],
|
|
65
|
-
|
|
66
|
-
security: {
|
|
67
|
-
trustProxy: true,
|
|
68
|
-
cookieSecret: process.env.JSKELET_SECRET,
|
|
69
|
-
csrf: {
|
|
70
|
-
enabled: true,
|
|
71
|
-
token: false,
|
|
72
|
-
allowedOrigins: [],
|
|
73
|
-
exclude: ["/webhook/:path*"],
|
|
74
|
-
cookieName: "csrf_token",
|
|
75
|
-
fieldName: "_csrf",
|
|
76
|
-
headerName: "x-csrf-token",
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
navigation: {
|
|
81
|
-
prefetch: "moderate",
|
|
82
|
-
prerender: "conservative",
|
|
83
|
-
viewTransition: true,
|
|
84
|
-
exclude: ["/cikis"],
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
prewarmSkip: ["/api/", "/_fragment/", "/__ornek/"],
|
|
88
|
-
watch: ["data"],
|
|
89
|
-
|
|
90
|
-
fonts: [{ family: "Inter", weights: [400, 600, 700] }],
|
|
91
|
-
icons: { dir: "icons", scan: ["views", "client", "routes", "lib"] },
|
|
92
|
-
images: { widths: [400, 800, 1200], quality: 78, skip: ["indirmeler"] },
|
|
93
|
-
clientEnv: ["PUBLIC_WS_URL"],
|
|
94
|
-
|
|
95
|
-
async headers() {
|
|
96
|
-
return [
|
|
97
|
-
{
|
|
98
|
-
source: "/:path*",
|
|
99
|
-
headers: [{ key: "X-Frame-Options", value: "SAMEORIGIN" }],
|
|
100
|
-
},
|
|
101
|
-
];
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
async redirects() {
|
|
105
|
-
return [{ source: "/eski/:slug", destination: "/yeni/:slug", permanent: true }];
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
async rewrites() {
|
|
109
|
-
return {
|
|
110
|
-
afterFiles: [
|
|
111
|
-
{ source: "/api/:path*", destination: "https://api.ornek.com/:path*" },
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
async cache() {
|
|
117
|
-
return {
|
|
118
|
-
html: { "/": 60, "/haber/:slug": 300 },
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
|
182
|
-
|
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
**>** `
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
},
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
`
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
|
283
|
-
|
|
|
284
|
-
| `
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
`[
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
bir
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
`
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
|
348
|
-
|
|
|
349
|
-
| `
|
|
350
|
-
| `
|
|
351
|
-
| `csrf.
|
|
352
|
-
| `csrf.
|
|
353
|
-
| `csrf.
|
|
354
|
-
| `csrf.
|
|
355
|
-
| `csrf.
|
|
356
|
-
| `csrf.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
`
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
|
384
|
-
|
|
|
385
|
-
| `
|
|
386
|
-
| `
|
|
387
|
-
| `
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
body >
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
::view-transition-old(site-
|
|
438
|
-
::view-transition-
|
|
439
|
-
::view-transition-new(site-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
::view-transition-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
**
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
|
498
|
-
|
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
{ family: "
|
|
510
|
-
]
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
|
522
|
-
|
|
|
523
|
-
| `{ dir: "
|
|
524
|
-
| `{
|
|
525
|
-
| `
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
`house
|
|
529
|
-
dizini
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
`
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
|
550
|
-
|
|
|
551
|
-
| `
|
|
552
|
-
| `
|
|
553
|
-
| `
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
|
559
|
-
|
|
|
560
|
-
| `
|
|
561
|
-
| `
|
|
562
|
-
| `
|
|
563
|
-
| `
|
|
564
|
-
| `
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
{ key: "X-
|
|
631
|
-
{ key: "
|
|
632
|
-
{
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
//
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
|
660
|
-
|
|
|
661
|
-
| `
|
|
662
|
-
| `
|
|
663
|
-
| `
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
- `
|
|
677
|
-
-
|
|
678
|
-
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
"/
|
|
701
|
-
"/
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
### `cache().
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
`
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
ve
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
|
1140
|
-
|
|
|
1141
|
-
| `
|
|
1142
|
-
| `
|
|
1143
|
-
| `
|
|
1144
|
-
| `
|
|
1145
|
-
| `
|
|
1146
|
-
| `
|
|
1147
|
-
| `
|
|
1148
|
-
| `
|
|
1149
|
-
| `
|
|
1150
|
-
| `
|
|
1151
|
-
| `
|
|
1152
|
-
| `
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1
|
+
# 07 — Yapılandırma referansı
|
|
2
|
+
|
|
3
|
+
Bu belge `jskelet.config.mjs`'in tam referansıdır: her alan, tipi, varsayılanı
|
|
4
|
+
ve örneği. Ardından `source` desen sözdizimi ve framework'ün okuduğu tüm ortam
|
|
5
|
+
değişkenleri tablosu geliyor. Alanların davranışsal ayrıntıları için ilgili
|
|
6
|
+
belgelere bağlantı verildi; buradaki amaç tek bakışta tam liste sunmak.
|
|
7
|
+
|
|
8
|
+
## Dosyanın konumu ve yüklenmesi
|
|
9
|
+
|
|
10
|
+
Config dosyası proje kökünde `jskelet.config.mjs` adıyla aranır ve **zorunlu
|
|
11
|
+
değildir**. Yoksa ya da okunamıyorsa uyarı basılır ve sunucu varsayılanlarla
|
|
12
|
+
ayağa kalkar; bozuk bir düzenleme siteyi açılamaz hâle getirmemeli.
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// jskelet.config.mjs
|
|
16
|
+
export default {
|
|
17
|
+
// …
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Default export yoksa modülün kendisi config olarak kullanılır (named export'lar).
|
|
22
|
+
|
|
23
|
+
`headers()`, `redirects()`, `rewrites()` ve `cache()` bölümleri fonksiyon **ya da
|
|
24
|
+
düz değer** olabilir; fonksiyon olmaları hâlinde `async` olabilirler ve `this`
|
|
25
|
+
config nesnesine bağlıdır. Bir bölüm hata verirse yalnızca o bölüm yok sayılır.
|
|
26
|
+
|
|
27
|
+
Config başarıyla yüklendiğinde bir özet basılır:
|
|
28
|
+
`[config] jskelet.config.mjs loaded — 3 headers, 2 redirects, 1 cache rule`
|
|
29
|
+
|
|
30
|
+
## Tam örnek
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// jskelet.config.mjs
|
|
34
|
+
export default {
|
|
35
|
+
paths: {
|
|
36
|
+
views: "views",
|
|
37
|
+
public: "public",
|
|
38
|
+
client: "client",
|
|
39
|
+
routes: "routes",
|
|
40
|
+
styles: "styles/globals.css",
|
|
41
|
+
generated: ".jskelet",
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
brand: {
|
|
45
|
+
name: "Örnek",
|
|
46
|
+
poweredBy: "Örnek",
|
|
47
|
+
cacheHeader: "X-Ornek-Cache",
|
|
48
|
+
devBasePath: "/__ornek/dev",
|
|
49
|
+
prewarmUserAgent: "ornek-prewarm",
|
|
50
|
+
devTokenCookie: "dev_token",
|
|
51
|
+
lang: "tr",
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
layout: "views/layout.jsk",
|
|
55
|
+
routes: ["./routes/10-pages.mjs", "./routes/99-catch-all.mjs"],
|
|
56
|
+
trailingSlash: false,
|
|
57
|
+
|
|
58
|
+
static: {
|
|
59
|
+
extensions: [".svg", ".png", ".webp", ".avif", ".ico", ".woff2"],
|
|
60
|
+
prefixes: ["/assets/", "/fonts/"],
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
devGateBypass: ["/api/healthcheck", "/robots.txt"],
|
|
64
|
+
preconnect: ["https://cdn.ornek.com"],
|
|
65
|
+
|
|
66
|
+
security: {
|
|
67
|
+
trustProxy: true,
|
|
68
|
+
cookieSecret: process.env.JSKELET_SECRET,
|
|
69
|
+
csrf: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
token: false,
|
|
72
|
+
allowedOrigins: [],
|
|
73
|
+
exclude: ["/webhook/:path*"],
|
|
74
|
+
cookieName: "csrf_token",
|
|
75
|
+
fieldName: "_csrf",
|
|
76
|
+
headerName: "x-csrf-token",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
navigation: {
|
|
81
|
+
prefetch: "moderate",
|
|
82
|
+
prerender: "conservative",
|
|
83
|
+
viewTransition: true,
|
|
84
|
+
exclude: ["/cikis"],
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
prewarmSkip: ["/api/", "/_fragment/", "/__ornek/"],
|
|
88
|
+
watch: ["data"],
|
|
89
|
+
|
|
90
|
+
fonts: [{ family: "Inter", weights: [400, 600, 700] }],
|
|
91
|
+
icons: { dir: "icons", scan: ["views", "client", "routes", "lib"] },
|
|
92
|
+
images: { widths: [400, 800, 1200], quality: 78, skip: ["indirmeler"] },
|
|
93
|
+
clientEnv: ["PUBLIC_WS_URL"],
|
|
94
|
+
|
|
95
|
+
async headers() {
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
source: "/:path*",
|
|
99
|
+
headers: [{ key: "X-Frame-Options", value: "SAMEORIGIN" }],
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async redirects() {
|
|
105
|
+
return [{ source: "/eski/:slug", destination: "/yeni/:slug", permanent: true }];
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
async rewrites() {
|
|
109
|
+
return {
|
|
110
|
+
afterFiles: [
|
|
111
|
+
{ source: "/api/:path*", destination: "https://api.ornek.com/:path*" },
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async cache() {
|
|
117
|
+
return {
|
|
118
|
+
html: { "/": 60, "/haber/:slug": 300 },
|
|
119
|
+
staleWhileRevalidate: 60,
|
|
120
|
+
query: { "/arama": ["q", "page"] },
|
|
121
|
+
maxEntries: 500,
|
|
122
|
+
data: { maxEntries: 10000, staleFactor: 10 },
|
|
123
|
+
prewarm: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
max: 400,
|
|
126
|
+
concurrency: 4,
|
|
127
|
+
rps: 0,
|
|
128
|
+
intervalSeconds: 0,
|
|
129
|
+
rotate: true,
|
|
130
|
+
priority: ["/", "/haber/:slug"],
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
hooks: {
|
|
136
|
+
metadata() { /* … */ },
|
|
137
|
+
layoutContext() { /* … */ },
|
|
138
|
+
notFound() { /* … */ },
|
|
139
|
+
error() { /* … */ },
|
|
140
|
+
prewarmPaths() { /* … */ },
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## `paths`
|
|
146
|
+
|
|
147
|
+
**Tip:** `Record<string, string>` — **Varsayılan:** aşağıdaki tablo
|
|
148
|
+
|
|
149
|
+
Proje kökündeki dizin (ve `styles` için dosya) adları. Değerler proje köküne
|
|
150
|
+
göre çözülür ve içeride mutlak yola çevrilir.
|
|
151
|
+
|
|
152
|
+
| Anahtar | Varsayılan | İçeriği |
|
|
153
|
+
| --- | --- | --- |
|
|
154
|
+
| `views` | `"views"` | Layout, sayfalar, bileşenler (klasik kök; `.jsk` / `.ejs`) |
|
|
155
|
+
| `features` | `"features"` | Feature-first dilimler (`<name>/{server,views,client}`) |
|
|
156
|
+
| `shared` | `"shared"` | Özellikler arası paylaşılan server/views/client |
|
|
157
|
+
| `public` | `"public"` | Statik dosyalar; build çıktısı da buraya yazılır |
|
|
158
|
+
| `client` | `"client"` | Island runtime kaynakları ve entry'ler |
|
|
159
|
+
| `routes` | `"routes"` | Route modülleri |
|
|
160
|
+
| `styles` | `"styles/globals.css"` | Tailwind/PostCSS giriş **dosyası** |
|
|
161
|
+
| `generated` | `".jskelet"` | `manifest.json`, `templates/`, `metafile.json`, `images.json` |
|
|
162
|
+
|
|
163
|
+
`styles` bir dosya yolu olduğu hâlde aynı çözümlemeden geçer; ayrı bir alan
|
|
164
|
+
tutmaya değmiyor.
|
|
165
|
+
|
|
166
|
+
İki yol her zaman türetilir ve ezilemez: `public/assets` (hash'li build
|
|
167
|
+
çıktısı) ve `public/fonts` (self-host fontlar).
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
paths: { views: "src/views", routes: "src/routes", styles: "src/styles/main.css" }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## `brand`
|
|
174
|
+
|
|
175
|
+
**Tip:** `object` — **Varsayılan:** aşağıdaki tablo
|
|
176
|
+
|
|
177
|
+
Markalama ve tek yerden değiştirilebilir isimler. Fork eden ya da beyaz etiket
|
|
178
|
+
kullanan projeler kendi adını verebilir. Verilen alanlar varsayılanlarla sığ
|
|
179
|
+
birleştirilir.
|
|
180
|
+
|
|
181
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
182
|
+
| --- | --- | --- | --- |
|
|
183
|
+
| `name` | `string` | `"JSkelet"` | Görüntü adı |
|
|
184
|
+
| `poweredBy` | `string` | `"JSkelet"` | `X-Powered-By` başlığının değeri |
|
|
185
|
+
| `cacheHeader` | `string` | `"X-JSkelet-Cache"` | HTML cache durumu başlığı ([06-cache.md](./06-cache.md)) |
|
|
186
|
+
| `devBasePath` | `string` | `"/__jskelet/dev"` | Dev overlay ve rapor uçlarının kökü |
|
|
187
|
+
| `prewarmUserAgent` | `string` | `"jskelet-prewarm"` | Isıtma isteklerinin UA'sı; dev paneli bunu filtreler |
|
|
188
|
+
| `devTokenCookie` | `string` | `"dev_token"` | Dev gate'in çerez ve query parametresi adı |
|
|
189
|
+
| `lang` | `string` | — | `<html lang>` varsayılanı. Verilmezse layout `"en"` kullanır. |
|
|
190
|
+
| `sharedCookieRoots` | `string[]` | `[]` | Paylaşımlı cookie Domain kökleri (örn. `.investvio.com`, `.localhost`). [12](./12-panel-ve-oturum.md) |
|
|
191
|
+
|
|
192
|
+
`lang` için öncelik sırası: `hooks.layoutContext()` → `lang` **>** `brand.lang`
|
|
193
|
+
**>** `"en"`.
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
brand: {
|
|
197
|
+
lang: "tr",
|
|
198
|
+
poweredBy: "Örnek",
|
|
199
|
+
sharedCookieRoots: [".investvio.com", ".localhost"],
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## `auth`
|
|
204
|
+
|
|
205
|
+
**Tip:** `object` — **Varsayılan:** `{ crossSubdomainHandoff: false }`
|
|
206
|
+
|
|
207
|
+
Kimlik framework'te yok; bu bölüm yalnızca alt alan adları arasında kısa
|
|
208
|
+
session id taşımak için handoff köprüsünü açar.
|
|
209
|
+
|
|
210
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
211
|
+
| --- | --- | --- | --- |
|
|
212
|
+
| `crossSubdomainHandoff` | `boolean \| object` | `false` | Açıkken `POST /_jskelet/auth/handoff` + `?handoff=` redeem. Object: `allowedCookieNames` (zorunlu), `ttlSeconds?`, `path?`, `maxValueBytes?`, `maxPendingTickets?`, `maxMintsPerIpPerMinute?` |
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
auth: {
|
|
216
|
+
crossSubdomainHandoff: {
|
|
217
|
+
allowedCookieNames: ["sid"],
|
|
218
|
+
ttlSeconds: 60,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Mint uç noktası CSRF middleware'inden **sonra** mount edilir (origin kontrolü).
|
|
224
|
+
Cookie adı allowlist dışındaysa veya RFC 6265 token değilse 400. Ayrıntı:
|
|
225
|
+
[12-panel-ve-oturum.md](./12-panel-ve-oturum.md).
|
|
226
|
+
|
|
227
|
+
## `layout`
|
|
228
|
+
|
|
229
|
+
**Tip:** `string` — **Varsayılan:** yok (otomatik çözüm)
|
|
230
|
+
|
|
231
|
+
Layout dosyasının yolu (`.jsk` veya legacy `.ejs`). Verilen değer **views
|
|
232
|
+
dizininin üst dizinine** göre çözülür, yani varsayılan `views` ile
|
|
233
|
+
`"views/ozel.jsk"` → `<root>/views/ozel.jsk`.
|
|
234
|
+
|
|
235
|
+
Verilmezse sırayla: `views/layout.jsk`, `views/layout.ejs` (legacy), yoksa
|
|
236
|
+
framework'ün `src/templates/layout.jsk` varsayılanı. Ayrıntı:
|
|
237
|
+
[04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md).
|
|
238
|
+
|
|
239
|
+
## `routes`
|
|
240
|
+
|
|
241
|
+
**Tip:** `string[]` — **Varsayılan:** `null` (dizin taraması)
|
|
242
|
+
|
|
243
|
+
Route modüllerinin açık listesi, proje köküne göre. Verilen sırada yüklenir.
|
|
244
|
+
Verilmezse `paths.routes` dizini alfabetik ve özyinelemeli olarak taranır.
|
|
245
|
+
Ayrıntı: [03-routing.md](./03-routing.md).
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
routes: ["./routes/api.js", "./routes/pages.js", "./routes/catch-all.js"]
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## `trailingSlash`
|
|
252
|
+
|
|
253
|
+
**Tip:** `boolean` — **Varsayılan:** `false`
|
|
254
|
+
|
|
255
|
+
`true` iken kanonik URL'ler `/` ile biter: `/hakkinda/` doğrudan **200**
|
|
256
|
+
döner; slash'sız `/hakkinda` **308** ile `/hakkinda/` adresine gider (301
|
|
257
|
+
değil — metodu koruyan kalıcı yönlendirme, framework'ün diğer `permanent`
|
|
258
|
+
redirect'leriyle aynı). Query string korunur.
|
|
259
|
+
|
|
260
|
+
İstisnalar: kök `/`, uzantılı dosya yolları (`/robots.txt`, `/assets/app.js`)
|
|
261
|
+
ve `/.well-known/**`. Bunlara slash eklenmez.
|
|
262
|
+
|
|
263
|
+
`false` iken (varsayılan) slash dayatılmaz. Express non-strict eşleşme ile
|
|
264
|
+
`/x` ve `/x/` ikisi de 200 olabilir — Next.js'in varsayılan "slash'ı kırp"
|
|
265
|
+
davranışından bilinçli fark; mevcut siteleri kırmamak için.
|
|
266
|
+
|
|
267
|
+
Açıkken şablonlardaki `href`, sitemap ve `redirects()` hedeflerini de
|
|
268
|
+
slash'lı yazın; aksi hâlde tarayıcı her tıklamada ekstra bir 308 görür.
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
trailingSlash: true
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## `static`
|
|
275
|
+
|
|
276
|
+
**Tip:** `{ extensions?: string[], prefixes?: string[] }` — **Varsayılan:**
|
|
277
|
+
aşağıda
|
|
278
|
+
|
|
279
|
+
Uzantı ve önek bazlı statik dosya tespiti. Bu listeye uyan yollara
|
|
280
|
+
`Cache-Control: public, max-age=31536000, immutable` yazılır.
|
|
281
|
+
|
|
282
|
+
| Alan | Varsayılan |
|
|
283
|
+
| --- | --- |
|
|
284
|
+
| `extensions` | `[".svg", ".png", ".webp", ".avif", ".ico", ".woff2"]` |
|
|
285
|
+
| `prefixes` | `["/assets/", "/fonts/"]` |
|
|
286
|
+
|
|
287
|
+
Verilirse varsayılanın **yerine** geçer (birleştirilmez), yani varsayılana ek
|
|
288
|
+
yapmak isterseniz tam listeyi yazın.
|
|
289
|
+
|
|
290
|
+
```js
|
|
291
|
+
static: {
|
|
292
|
+
extensions: [".svg", ".png", ".webp", ".avif", ".ico", ".woff2", ".mp4"],
|
|
293
|
+
prefixes: ["/assets/", "/fonts/", "/video/"],
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## `devGate`
|
|
298
|
+
|
|
299
|
+
**Tip:** `boolean` — **Varsayılan:** `false`
|
|
300
|
+
|
|
301
|
+
Yayına açılmamış ortamı gizler. **`DEV_TOKEN` tek başına siteyi kilitlemez.**
|
|
302
|
+
Paylaşılan bir task tanımı production'a da aynı değişkeni taşıyabilir; o
|
|
303
|
+
durumda ziyaretçi token vermek zorunda kalmaz, site açık kalır.
|
|
304
|
+
|
|
305
|
+
Gate'i açmak için `devGate: true` ya da `DEV_GATE=1`. İkisi de varken token
|
|
306
|
+
taşımayan isteğe 404 döner. `DEV_GATE=0` config'teki açığı da kapatır. Token
|
|
307
|
+
boşsa gate açık olsa da istekler geçer.
|
|
308
|
+
|
|
309
|
+
Ayrıntı: [09-dev-araclari.md](./09-dev-araclari.md).
|
|
310
|
+
|
|
311
|
+
## `devGateBypass`
|
|
312
|
+
|
|
313
|
+
**Tip:** `string[]` — **Varsayılan:**
|
|
314
|
+
`["/api/healthcheck", "/robots.txt", "/sitemap.xml", "/site.webmanifest", "/favicon.ico"]`
|
|
315
|
+
|
|
316
|
+
Dev gate'in hiçbir koşulda kapatmadığı **tam** yollar (önek değil, birebir
|
|
317
|
+
eşleşme). Gate açıkken sağlık kontrolünün ve robots dosyalarının erişilebilir
|
|
318
|
+
kalması için. Verilirse varsayılanın yerine geçer.
|
|
319
|
+
|
|
320
|
+
Ayrıntı: [09-dev-araclari.md](./09-dev-araclari.md).
|
|
321
|
+
|
|
322
|
+
## `preconnect`
|
|
323
|
+
|
|
324
|
+
**Tip:** `string[]` — **Varsayılan:** `[]`
|
|
325
|
+
|
|
326
|
+
Üçüncü taraf origin'ler; her sayfanın `<head>`inde `<link rel="preconnect">`
|
|
327
|
+
olarak basılır. Görsel CDN'i, API origin'i, font host'u buraya yazılır. Değerler
|
|
328
|
+
`new URL(...).origin` ile normalize edilir; geçersiz bir URL atlanır ve uyarı
|
|
329
|
+
basılır.
|
|
330
|
+
|
|
331
|
+
Liste her sayfada aynı olduğu için bir kez hesaplanıp saklanır. Boş liste geçerli
|
|
332
|
+
bir yapılandırmadır.
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
preconnect: ["https://cdn.ornek.com", "https://api.ornek.com"]
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## `security`
|
|
339
|
+
|
|
340
|
+
**Tip:** `object` — **Varsayılan:**
|
|
341
|
+
`{ trustProxy: true, cookieSecret: null, csrf: { enabled: true, token: false, … } }`
|
|
342
|
+
|
|
343
|
+
Kişiye özel sayfaların tamamı ve gerekçeleri
|
|
344
|
+
[12-panel-ve-oturum.md](./12-panel-ve-oturum.md)'de; burada alanların referansı
|
|
345
|
+
var.
|
|
346
|
+
|
|
347
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
348
|
+
| --- | --- | --- | --- |
|
|
349
|
+
| `trustProxy` | `boolean` | `true` | Express'in `trust proxy` ayarı. Ters proxy arkasında doğru protokol ve istemci IP'si için gerekli. |
|
|
350
|
+
| `cookieSecret` | `string \| null` | `null` | İmzalı cookie sırrı. Verilmezse `JSKELET_SECRET` okunur. |
|
|
351
|
+
| `csrf.enabled` | `boolean` | `true` | Origin/`Sec-Fetch-Site` kontrolü. |
|
|
352
|
+
| `csrf.token` | `boolean` | `false` | Çift gönderim token'ı katmanı. Cookie oturumlu formlarda **açın**. |
|
|
353
|
+
| `csrf.allowedOrigins` | `string[]` | `[]` | Kendi host'umuzun yanında kabul edilen origin'ler. |
|
|
354
|
+
| `csrf.exclude` | `string[]` | `[]` | Kontrolden muaf yollar; `source` desen sözdizimi. |
|
|
355
|
+
| `csrf.cookieName` | `string` | `"csrf_token"` | Token cookie'sinin adı. |
|
|
356
|
+
| `csrf.fieldName` | `string` | `"_csrf"` | `csrfField()`in bastığı alan adı. |
|
|
357
|
+
| `csrf.headerName` | `string` | `"x-csrf-token"` | Token'ın kabul edildiği başlık. |
|
|
358
|
+
|
|
359
|
+
`trustProxy` doğrudan internete açık bir sunucuda **kapatılmalı**: açıkken
|
|
360
|
+
istemci kendi `X-Forwarded-For` / `X-Forwarded-Proto` / Host başlığını
|
|
361
|
+
uydurabilir; rate limit, admin IP allowlist, Secure cookie ve cache `vary.host`
|
|
362
|
+
yanlış adresi görür. Ters proxy (nginx, Caddy, Cloudflare) arkasındaysa `true`
|
|
363
|
+
doğru varsayılandır.
|
|
364
|
+
|
|
365
|
+
CSRF kontrolü yalnızca çapraz site olduğu **belli** olan istekleri reddeder —
|
|
366
|
+
`Origin` uyuşmuyorsa ya da `Sec-Fetch-Site: cross-site` geldiyse. İkisi de yoksa
|
|
367
|
+
istek geçer, çünkü tarayıcılar çapraz origin bir POST'ta `Origin`'i her zaman
|
|
368
|
+
gönderirken webhook'lar hiç göndermez. Cookie ile oturum açan panel/form
|
|
369
|
+
uygulamalarında `csrf.token: true` + `csrfField()` ikinci katmandır; webhook
|
|
370
|
+
uçlarını `csrf.exclude` listesine yazın.
|
|
371
|
+
|
|
372
|
+
## `navigation`
|
|
373
|
+
|
|
374
|
+
**Tip:** `object` — **Varsayılan:**
|
|
375
|
+
`{ prefetch: "moderate", prerender: false, viewTransition: false, exclude: [] }`
|
|
376
|
+
|
|
377
|
+
Site içi gezinmeyi hızlandıran `<head>` ipuçları. JSkelet klasik MPA olduğu için
|
|
378
|
+
her tıklama tam sayfa yüklemesidir; bu bölüm o yüklemeyi tarayıcının **önceden**
|
|
379
|
+
yapmasını sağlar. Client runtime'ı eklenmez — Speculation Rules ve view
|
|
380
|
+
transition tarayıcı yetenekleridir, desteklemeyen tarayıcıda sessizce yok
|
|
381
|
+
sayılırlar.
|
|
382
|
+
|
|
383
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
384
|
+
| --- | --- | --- | --- |
|
|
385
|
+
| `prefetch` | `false \| "conservative" \| "moderate" \| "eager"` | `"moderate"` | Bağlantı hedefinin **belgesini** önceden indirir |
|
|
386
|
+
| `prerender` | aynı | `false` | Hedefi arka planda **tam render eder**; tıklama anında açılır |
|
|
387
|
+
| `viewTransition` | `boolean` | `false` | `@view-transition { navigation: auto }` basar |
|
|
388
|
+
| `exclude` | `string[]` | `[]` | Spekülasyon dışı bırakılacak href desenleri |
|
|
389
|
+
|
|
390
|
+
`true` verilirse `prefetch`/`prerender` varsayılan eagerness'a düşer; tanınmayan
|
|
391
|
+
bir değer uyarı basıp varsayılana döner.
|
|
392
|
+
|
|
393
|
+
**Eagerness ne demek:** `conservative` bağlantıya basıldığı an, `moderate`
|
|
394
|
+
bağlantı üzerinde bir süre duraksandığında, `eager` bağlantı görünür olur olmaz
|
|
395
|
+
tetikler. Yukarı çıktıkça isabet artar, boşa giden istek de artar.
|
|
396
|
+
|
|
397
|
+
**`prerender` neden kapalı geliyor.** Prerender edilen sayfanın script'leri
|
|
398
|
+
gerçekten çalışır. Ölçüm kodunu `prerenderingchange` olayına bağlamayan bir
|
|
399
|
+
uygulamada ziyaret sayıları şişer. Açmadan önce analytics'i gözden geçirin;
|
|
400
|
+
sunucu tarafındaki maliyeti düşüktür, çünkü spekülatif istek de HTML
|
|
401
|
+
önbelleğinden karşılanır ([06-cache.md](./06-cache.md)).
|
|
402
|
+
|
|
403
|
+
**Her koşulda muaf olanlar.** `/api/*`, `/_fragment/*` ve `brand.devBasePath`
|
|
404
|
+
altındaki yollar otomatik dışlanır; `exclude` bunların üstüne eklenir. Ayrıca
|
|
405
|
+
`rel="nofollow"`, `target="_blank"` ve `data-no-prefetch` taşıyan bağlantılar
|
|
406
|
+
hiçbir kurala girmez. Yan etkisi olan tek bir bağlantıyı dışarıda bırakmanın en
|
|
407
|
+
kolay yolu sonuncusu:
|
|
408
|
+
|
|
409
|
+
```html
|
|
410
|
+
<a href="/cikis" data-no-prefetch>Çıkış</a>
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**`viewTransition` açarken arka planı `<html>`e verin.** Geçiş sırasında tarayıcı
|
|
414
|
+
eski ve yeni sayfanın anlık görüntülerini çapraz geçirir; `<body>`ye verilmiş bir
|
|
415
|
+
arka plan bu görüntünün içinde kalır ve altta kalan canvas görünür. Sonuç, her
|
|
416
|
+
geçişte bir kare beyaz flaştır ve koyu temada gözden kaçmaz. Renk `<html>` (ya da
|
|
417
|
+
`:root`) üzerindeyse böyle bir boşluk oluşmaz:
|
|
418
|
+
|
|
419
|
+
```html
|
|
420
|
+
<html lang="tr" class="bg-white dark:bg-slate-950">
|
|
421
|
+
<body class="text-slate-900 dark:text-slate-100">
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Hareket azaltma tercihi framework tarafından karşılanır: `prefers-reduced-motion:
|
|
425
|
+
reduce` altında geçiş kapatılır, ayrıca bir şey yazmanız gerekmez.
|
|
426
|
+
|
|
427
|
+
**Geçişi içerikle sınırlayın.** Varsayılan davranış tüm belgeyi tek parça olarak
|
|
428
|
+
çapraz geçirir, yani gezinme boyunca hiç değişmeyen header ve footer da titrer.
|
|
429
|
+
Bu bölgelere bir `view-transition-name` vermek onları kendi grubuna alır;
|
|
430
|
+
tarayıcı aynı adı iki belgede de gördüğü için "aynı öğe" sayar. Adlandırılan
|
|
431
|
+
öğenin animasyonunu kapatınca geçiş yalnızca içerikte kalır:
|
|
432
|
+
|
|
433
|
+
```css
|
|
434
|
+
body > header { view-transition-name: site-header; }
|
|
435
|
+
body > footer { view-transition-name: site-footer; }
|
|
436
|
+
|
|
437
|
+
::view-transition-old(site-header),
|
|
438
|
+
::view-transition-old(site-footer) { animation: none; opacity: 0; }
|
|
439
|
+
::view-transition-new(site-header),
|
|
440
|
+
::view-transition-new(site-footer) { animation: none; opacity: 1; }
|
|
441
|
+
|
|
442
|
+
/* Kalan içerik; varsayılan 250ms gezinmeyi yavaş hissettiriyor. */
|
|
443
|
+
::view-transition-old(root),
|
|
444
|
+
::view-transition-new(root) { animation-duration: 180ms; }
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Çalışan bir örnek için Tailwind `@source` ve view-transition CSS'ini kendi
|
|
448
|
+
uygulamanızın `styles/globals.css` dosyasına taşıyın; yukarıdaki bloklar
|
|
449
|
+
başlangıç noktasıdır.
|
|
450
|
+
|
|
451
|
+
**CSP kullanıyorsanız** kurallar satır içi bir `<script type="speculationrules">`
|
|
452
|
+
olarak basılır; `script-src` politikanızın buna izin vermesi gerekir.
|
|
453
|
+
|
|
454
|
+
```js
|
|
455
|
+
navigation: {
|
|
456
|
+
prefetch: "moderate",
|
|
457
|
+
prerender: "conservative",
|
|
458
|
+
viewTransition: true,
|
|
459
|
+
exclude: ["/cikis", "/sepet/*"],
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## `prewarmSkip`
|
|
464
|
+
|
|
465
|
+
**Tip:** `string[]` — **Varsayılan:** `["/api/", "/_fragment/", "/__jskelet/"]`
|
|
466
|
+
|
|
467
|
+
Isıtmanın atlayacağı yol **önekleri**. Oturuma bağlı ya da fragment uçları
|
|
468
|
+
ısıtılmamalı. Verilirse varsayılanın yerine geçer — `brand.devBasePath`i
|
|
469
|
+
değiştirdiyseniz bu listeyi de güncellemeyi unutmayın.
|
|
470
|
+
|
|
471
|
+
Ayrıntı: [06-cache.md](./06-cache.md).
|
|
472
|
+
|
|
473
|
+
## `watch`
|
|
474
|
+
|
|
475
|
+
**Tip:** `string[]` — **Varsayılan:** `[]`
|
|
476
|
+
|
|
477
|
+
`jskelet dev`in sunucu yeniden başlatma için izleyeceği **ek** dizinler, proje
|
|
478
|
+
köküne göre. `routes`, `views` ve `lib` zaten izlenir; `client/` ve `styles/`
|
|
479
|
+
esbuild ve CSS watcher'ları tarafından ele alınır, buraya konmamalı.
|
|
480
|
+
|
|
481
|
+
Yalnızca `.js`, `.mjs`, `.json` ve `.ejs` uzantılı dosyalar tetikleyicidir.
|
|
482
|
+
|
|
483
|
+
```js
|
|
484
|
+
watch: ["data", "content"]
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Ayrıntı: [09-dev-araclari.md](./09-dev-araclari.md).
|
|
488
|
+
|
|
489
|
+
## `fonts`
|
|
490
|
+
|
|
491
|
+
**Tip:** `{ family: string, slug?: string, weights?: number[] }[]` —
|
|
492
|
+
**Varsayılan:** `[]`
|
|
493
|
+
|
|
494
|
+
Self-host edilecek Google Fonts aileleri. Boş bırakılırsa font adımı hiç
|
|
495
|
+
çalışmaz.
|
|
496
|
+
|
|
497
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
498
|
+
| --- | --- | --- | --- |
|
|
499
|
+
| `family` | `string` | — | Google Fonts aile adı: `"Inter"`, `"Noto Sans"` |
|
|
500
|
+
| `slug` | `string` | `family`den türetilir (küçük harf, boşluk → `-`) | Dosya adı öneki |
|
|
501
|
+
| `weights` | `number[]` | `[400]` | İndirilecek ağırlıklar |
|
|
502
|
+
|
|
503
|
+
Çıktı: `public/fonts/<slug>-<weight>.woff2`, manifest anahtarı aynı dosya adı.
|
|
504
|
+
Dosyalar **sabit isimlidir** (hash yok) ve **commit edilmesi beklenir**.
|
|
505
|
+
Ayrıntı: [08-build.md](./08-build.md).
|
|
506
|
+
|
|
507
|
+
```js
|
|
508
|
+
fonts: [
|
|
509
|
+
{ family: "Inter", weights: [400, 600, 700] },
|
|
510
|
+
{ family: "Noto Serif", slug: "serif", weights: [400] },
|
|
511
|
+
]
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## `icons`
|
|
515
|
+
|
|
516
|
+
**Tip:** `{ scan?: string[], dir?: string } | false` — **Varsayılan:** `{ dir: "icons" }`
|
|
517
|
+
|
|
518
|
+
SVG ikon sprite üretimi. Kaynak **XOR** seçilir: `icons.dir` dizini varsa
|
|
519
|
+
yalnızca oradaki düz SVG'ler; yoksa `@phosphor-icons/core` (kuruluysa).
|
|
520
|
+
|
|
521
|
+
| Değer | Sonuç |
|
|
522
|
+
| --- | --- |
|
|
523
|
+
| `{}` (varsayılan) | `dir: "icons"`; taranan dizinler `["views", "client", "routes", "lib", "features", "shared"]` |
|
|
524
|
+
| `{ dir: "assets/icons" }` | Yerel SVG kökü değiştirilir |
|
|
525
|
+
| `{ scan: [...] }` | Taranan dizinler değiştirilir |
|
|
526
|
+
| `false` | Sprite adımı tamamen atlanır |
|
|
527
|
+
|
|
528
|
+
Yerel dizin (varsa) düz dosya adları kullanır: `house.svg` → `house:regular`,
|
|
529
|
+
`house-bold.svg` → `house:bold`. Boş bir `icons/` dizini Phosphor'a düşmez —
|
|
530
|
+
dizini silmek fallback'i açar. Ayrıntı: [08-build.md](./08-build.md).
|
|
531
|
+
|
|
532
|
+
```js
|
|
533
|
+
icons: {
|
|
534
|
+
dir: "icons",
|
|
535
|
+
scan: ["views", "client", "routes", "lib", "content"],
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
## `images`
|
|
540
|
+
|
|
541
|
+
**Tip:**
|
|
542
|
+
`{ widths?: number[], quality?: number, skip?: string[], remote?: { allowHosts: string[], path?: string, maxWidth?: number, cacheMaxAge?: number, fetchTimeoutMs?: number, maxBytes?: number } | false } | false`
|
|
543
|
+
— **Varsayılan:** `{ widths, quality, skip, remote: false }` (remote kapalı)
|
|
544
|
+
|
|
545
|
+
`public/` altındaki png/jpg görsellerin webp varyantlarını **build**'de üretir.
|
|
546
|
+
`remote.allowHosts` verilirse çalışma anında uzak görselleri de proxy eder
|
|
547
|
+
(`/_jskelet/image?url=&w=&q=` → webp).
|
|
548
|
+
|
|
549
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
550
|
+
| --- | --- | --- | --- |
|
|
551
|
+
| `widths` | `number[]` | `[400, 640, 960, 1280, 1920]` | Build ve remote `srcset` adayları. Kaynaktan büyük olanlar build'de elenir; kaynağın kendi genişliği (en fazla 1920) her zaman eklenir. |
|
|
552
|
+
| `quality` | `number` | `78` | webp kalitesi. Build'de imzaya girer; remote uçta `q` varsayılanı. |
|
|
553
|
+
| `skip` | `string[]` | `[]` | Build'de taranmayacak **dizin adları**. `assets` ve `fonts` her zaman atlanır. |
|
|
554
|
+
| `remote` | `object \| false` | kapalı | Runtime optimizer. `allowHosts` **zorunlu**; boşsa uç mount edilmez. |
|
|
555
|
+
|
|
556
|
+
### `images.remote`
|
|
557
|
+
|
|
558
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
559
|
+
| --- | --- | --- | --- |
|
|
560
|
+
| `allowHosts` | `string[]` | `[]` | Çekilebilecek host'lar. `*.cdn.example.com` sonek jokerini destekler. |
|
|
561
|
+
| `path` | `string` | `/_jskelet/image` | Optimizer GET yolu. |
|
|
562
|
+
| `maxWidth` | `number` | `1920` | `w` üst sınırı. |
|
|
563
|
+
| `cacheMaxAge` | `number` | `2592000` (30 gün) | Yanıt `Cache-Control` max-age (saniye). Disk önbelleği `.jskelet/image-cache/`; 256 MB'yi geçince en eski dosya düşer. |
|
|
564
|
+
| `fetchTimeoutMs` | `number` | `10000` | Upstream fetch zaman aşımı. |
|
|
565
|
+
| `maxBytes` | `number` | `10485760` (10 MiB) | Upstream gövde üst sınırı. |
|
|
566
|
+
|
|
567
|
+
`false` verilirse görsel adımı hiç çalışmaz. Build adımı `sharp` gerektirir ve
|
|
568
|
+
watch turunda hiç çalışmaz. Remote açıksa `sharp` **runtime**'da da gerekir;
|
|
569
|
+
yoksa optimizer kaynak URL'ye 302 yönlendirir. Fetch, redirect'leri otomatik
|
|
570
|
+
takip etmez: her hop `allowHosts` ve private adres kontrolünden geçer.
|
|
571
|
+
Ayrıntı: [08-build.md](./08-build.md).
|
|
572
|
+
|
|
573
|
+
```js
|
|
574
|
+
images: {
|
|
575
|
+
widths: [400, 800, 1200],
|
|
576
|
+
quality: 82,
|
|
577
|
+
skip: ["indirmeler"],
|
|
578
|
+
remote: {
|
|
579
|
+
allowHosts: ["static.ornek.com", "*.cdn.ornek.com"],
|
|
580
|
+
},
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
`image({ src: "https://static.ornek.com/a.jpg", width: 96, alt: "…" })` bu
|
|
585
|
+
ayarla `src` / `srcset`'i `/_jskelet/image?url=…&w=96` biçimine çevirir.
|
|
586
|
+
Elle URL kurmak için `remoteImageUrl(src, { width })` (`jskelet`).
|
|
587
|
+
|
|
588
|
+
## `clientEnv`
|
|
589
|
+
|
|
590
|
+
**Tip:** `string[]` — **Varsayılan:** `[]`
|
|
591
|
+
|
|
592
|
+
Client bundle'a build zamanında gömülecek ortam değişkeni anahtarları. Next'teki
|
|
593
|
+
`NEXT_PUBLIC_*` ile aynı sözleşme, ama hangi anahtarın herkese açık olduğu
|
|
594
|
+
isimden değil config'ten belli. `NODE_ENV` her zaman gömülür.
|
|
595
|
+
|
|
596
|
+
`process.env`in tamamı tek nesne olarak define edildiği için listede olmayan bir
|
|
597
|
+
anahtar okunduğunda çökme yerine `undefined` döner.
|
|
598
|
+
|
|
599
|
+
```js
|
|
600
|
+
clientEnv: ["PUBLIC_WS_URL", "PUBLIC_CDN_ORIGIN"]
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**Buraya gizli anahtar koymayın** — değerler bundle'da düz metin olarak durur.
|
|
604
|
+
İsimlerinde `SECRET`, `PASSWORD`, `TOKEN`, `API_KEY`, `PRIVATE` vb. geçen
|
|
605
|
+
anahtarlar build sırasında **reddeder** (`PUBLIC` / `PUBLISHABLE` içerenler
|
|
606
|
+
muaf).
|
|
607
|
+
|
|
608
|
+
## `headers()`
|
|
609
|
+
|
|
610
|
+
**Tip:** `() => { source: string, headers: { key: string, value: string }[] }[]`
|
|
611
|
+
— **Varsayılan:** `[]`
|
|
612
|
+
|
|
613
|
+
Yol desenine göre yanıt başlıkları. Framework yalnızca statik dosyalara uzun
|
|
614
|
+
ömürlü cache yazar; bunun dışındaki her başlık (CSP, COOP, HSTS,
|
|
615
|
+
X-Frame-Options…) buradan gelir ve varsayılanların üstüne biner. Üretim
|
|
616
|
+
sitelerinde en azından aşağıdaki güvenlik başlıklarını tanımlayın.
|
|
617
|
+
|
|
618
|
+
Eşleşen **tüm** kurallar uygulanır (redirect'lerin aksine ilk eşleşmede
|
|
619
|
+
durulmaz), sırayla; aynı başlığı iki kural yazarsa sonraki kazanır.
|
|
620
|
+
|
|
621
|
+
`key`i olmayan ya da `value`u `undefined` olan girdiler atlanır; hiç geçerli
|
|
622
|
+
başlığı kalmayan bir kural hiç eklenmez.
|
|
623
|
+
|
|
624
|
+
```js
|
|
625
|
+
async headers() {
|
|
626
|
+
return [
|
|
627
|
+
{
|
|
628
|
+
source: "/:path*",
|
|
629
|
+
headers: [
|
|
630
|
+
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
|
|
631
|
+
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
632
|
+
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
633
|
+
{
|
|
634
|
+
key: "Permissions-Policy",
|
|
635
|
+
value: "camera=(), microphone=(), geolocation=()",
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
key: "Content-Security-Policy",
|
|
639
|
+
value: "default-src 'self'; img-src 'self' https://cdn.ornek.com data:; script-src 'self'",
|
|
640
|
+
},
|
|
641
|
+
// Yalnızca HTTPS terminasyonu sizin kontrolünüzdeyse:
|
|
642
|
+
// { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
|
|
643
|
+
],
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
source: "/indirme/:path*",
|
|
647
|
+
headers: [{ key: "Cache-Control", value: "no-store" }],
|
|
648
|
+
},
|
|
649
|
+
];
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## `redirects()`
|
|
654
|
+
|
|
655
|
+
**Tip:**
|
|
656
|
+
`() => { source: string, destination: string, permanent?: boolean, statusCode?: number }[]`
|
|
657
|
+
— **Varsayılan:** `[]`
|
|
658
|
+
|
|
659
|
+
| Alan | Tip | Anlamı |
|
|
660
|
+
| --- | --- | --- |
|
|
661
|
+
| `source` | `string` | Desen (aşağıdaki sözdizimi) |
|
|
662
|
+
| `destination` | `string` | Hedef; `:param` yer tutucuları doldurulur |
|
|
663
|
+
| `permanent` | `boolean` | `true` → 308, aksi hâlde 307 |
|
|
664
|
+
| `statusCode` | `number` | Açık durum kodu; `permanent`i ezer |
|
|
665
|
+
|
|
666
|
+
İlk eşleşen kural kazanır ve query string korunur. Ayrıntı:
|
|
667
|
+
[03-routing.md](./03-routing.md).
|
|
668
|
+
|
|
669
|
+
## `rewrites()`
|
|
670
|
+
|
|
671
|
+
**Tip:** `() => Rule[] | { beforeFiles?: Rule[], afterFiles?: Rule[] }`
|
|
672
|
+
burada `Rule = { source: string, destination: string }` — **Varsayılan:** `[]`
|
|
673
|
+
|
|
674
|
+
Dizi döndürülürse tamamı `afterFiles` sayılır.
|
|
675
|
+
|
|
676
|
+
- `beforeFiles` statik dosyalardan da önce çalışır.
|
|
677
|
+
- `afterFiles` statik denendikten sonra, route'lardan önce çalışır.
|
|
678
|
+
- Mutlak hedef (`http://`/`https://`) → gömülü ters proxy.
|
|
679
|
+
- Göreli hedef → yalnızca `req.url` değişir.
|
|
680
|
+
|
|
681
|
+
Ayrıntı: [03-routing.md](./03-routing.md).
|
|
682
|
+
|
|
683
|
+
## `cache()`
|
|
684
|
+
|
|
685
|
+
**Tip:**
|
|
686
|
+
`() => { html?: Record<string, number>, staleWhileRevalidate?: number, query?: Record<string, string[] | true>, vary?: { host?: boolean, headers?: string[], fn?: (req) => string | null }, maxEntries?: number, data?: object, trackUpstream?: boolean, trackDependencies?: boolean, transientRetry?: object | false, upstream?: object, redis?: object, prewarm?: object }` —
|
|
687
|
+
**Varsayılan:**
|
|
688
|
+
`{ html: {}, staleWhileRevalidate: 60, query: {}, vary: { host: false }, maxEntries: 500, data: { maxEntries: 10000, staleFactor: 10 }, trackUpstream: true, trackDependencies: true, transientRetry: { attempts: 1, delayMs: 300 }, upstream: { rate: 0 }, redis: { enabled: false }, prewarm: { enabled: true, max: 400, intervalSeconds: 0, origins: [] } }`
|
|
689
|
+
|
|
690
|
+
### `cache().html`
|
|
691
|
+
|
|
692
|
+
Desen → saniye eşlemesi. Eşleşen kural, route'un kendi `revalidate` değerini
|
|
693
|
+
**ezer**. Negatif ya da sonlu olmayan değerler yok sayılır; `0` "önbellekleme"
|
|
694
|
+
anlamına gelir. TTL dolmadan önce framework, son render süresine göre erken
|
|
695
|
+
arka plan tazelemesi başlatır (ayrı bir config alanı yok; ayrıntı
|
|
696
|
+
[06-cache.md](./06-cache.md)).
|
|
697
|
+
|
|
698
|
+
```js
|
|
699
|
+
html: {
|
|
700
|
+
"/": 60,
|
|
701
|
+
"/haber/:slug": 300,
|
|
702
|
+
"/arama": 0,
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Tek istisna `route(fn, { private: true })`: bu route'ta desen eşleşse bile yok
|
|
707
|
+
sayılır. Kilidin tek yönlü olması bilinçli — ters yönde bir hata, bir
|
|
708
|
+
kullanıcının HTML'inin bir başkasına servis edilmesi anlamına geliyor.
|
|
709
|
+
|
|
710
|
+
### `cache().staleWhileRevalidate`
|
|
711
|
+
|
|
712
|
+
**Tip:** `number` — **Varsayılan:** `60`
|
|
713
|
+
|
|
714
|
+
Edge'in taze penceresi (`cache().html` / `revalidate`) bittikten sonra eski
|
|
715
|
+
HTML'i sunacağı süre, saniye. `CDN-Cache-Control` üzerindeki
|
|
716
|
+
`stale-while-revalidate` direktifine yazılır. `0` ise direktif basılmaz.
|
|
717
|
+
Süreç içi HTML önbelleğinin stale penceresini değiştirmez. Ayrıntı:
|
|
718
|
+
[06-cache.md](./06-cache.md).
|
|
719
|
+
|
|
720
|
+
### `cache().query`
|
|
721
|
+
|
|
722
|
+
Desen → cache anahtarına girmesine izin verilen query parametreleri.
|
|
723
|
+
|
|
724
|
+
**Varsayılan olarak query parametresi taşıyan istek dinamiktir**: `cache().html`
|
|
725
|
+
o yolu kapsıyor olsa bile HTML önbelleğine hiç girmez, `private, no-store` ile
|
|
726
|
+
gider. Sebebi basit — bir yolun bütün varyantlarını cache'lemek `?utm_source=…`
|
|
727
|
+
gibi sonsuz sayıda anahtar üretiyor ve `maxEntries` sınırına dayandığında
|
|
728
|
+
LRU'daki gerçek sayfaları dışarı atıyor. Hangi parametrenin çıktıyı gerçekten
|
|
729
|
+
değiştirdiğini yalnızca uygulama bilir.
|
|
730
|
+
|
|
731
|
+
```js
|
|
732
|
+
query: {
|
|
733
|
+
"/arama": ["q", "page"], // yalnızca bu ikisi anahtara girer
|
|
734
|
+
"/urunler": ["kategori"],
|
|
735
|
+
"/rapor/:id": true, // bütün parametreler anahtara girer
|
|
736
|
+
"/kampanya": [], // query tamamen yok sayılır
|
|
737
|
+
}
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
- **İzin listesi** (`string[]`): listedeki parametreler anahtara girer, her
|
|
741
|
+
farklı değer kendi girdisini alır. Listede olmayan parametreler **yok
|
|
742
|
+
sayılır** — sayfa yine cache'lenir ve bütün kampanya varyantları tek kopyayı
|
|
743
|
+
paylaşır.
|
|
744
|
+
- **`true`**: bütün parametreler anahtara girer. Anahtar sayısını sınırlayan
|
|
745
|
+
tek şey `maxEntries` olur; yalnızca değer kümesi kapalı olan yollarda kullan.
|
|
746
|
+
- **`[]`**: query hiç dikkate alınmaz, bütün varyantlar query'siz sürümün
|
|
747
|
+
HTML'ini alır.
|
|
748
|
+
|
|
749
|
+
Parametreler anahtara **sıralı** yazılır: `?a=1&b=2` ile `?b=2&a=1` aynı girdiyi
|
|
750
|
+
paylaşır. `route(fn, { private: true })` bu bölümden etkilenmez; private route
|
|
751
|
+
hiçbir koşulda cache'lenmez.
|
|
752
|
+
|
|
753
|
+
### `cache().vary`
|
|
754
|
+
|
|
755
|
+
HTML cache anahtarına query allowlist'ten **bağımsız** sabit parçalar ekler.
|
|
756
|
+
Host'tan locale üreten sitelerde `host: true` **zorunlu**; aksi halde ilk
|
|
757
|
+
locale'in HTML'i diğer host'a servis edilir. CDN zaten tam URL ile ayırır —
|
|
758
|
+
bu ayar origin L1 ve Redis HTML anahtarı içindir.
|
|
759
|
+
|
|
760
|
+
```js
|
|
761
|
+
vary: {
|
|
762
|
+
host: true, // h=tr.example.com|…
|
|
763
|
+
// headers: ["x-locale"],
|
|
764
|
+
// fn: (req) => req.hostname.startsWith("tr.") ? "l=tr" : "l=en",
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
769
|
+
| --- | --- | --- | --- |
|
|
770
|
+
| `host` | `boolean` | `false` | Public Host (`x-forwarded-host` yoksa `Host`), lowercase, portsuz → `h=…` |
|
|
771
|
+
| `headers` | `string[]` | `[]` | İstek başlıkları `ad=değer` olarak eklenir |
|
|
772
|
+
| `fn` | `(req) => string \| null` | — | Dönüş bir segment olarak eklenir |
|
|
773
|
+
|
|
774
|
+
Anahtar biçimi: `${vary}|${yol}?${query}` (vary yoksa önek yok). Ayrıntı:
|
|
775
|
+
[06-cache.md](./06-cache.md).
|
|
776
|
+
|
|
777
|
+
### `cache().maxEntries`
|
|
778
|
+
|
|
779
|
+
**Tip:** `number` — **Varsayılan:** `500`
|
|
780
|
+
|
|
781
|
+
HTML önbelleğinin girdi sınırı. Girdi başına yüz kilobayt düştüğü için bu sayıyı
|
|
782
|
+
yükseltmek belleği hızla tüketir; on binlerce yollu bir siteyi buradan çözmeye
|
|
783
|
+
çalışmak yanlış katman, doğru yer `cache().data`.
|
|
784
|
+
|
|
785
|
+
**Tavan 800.** Daha yükseği yüklemede uyarıyla 800'e çekilir. Süreç içi HTML +
|
|
786
|
+
sıkıştırılmış gövde ayrıca 256 MB'yi geçemez; bu bütçe config'den yükseltilmez.
|
|
787
|
+
Sıkıştırılmış kopya tektir (brotli veya gzip).
|
|
788
|
+
|
|
789
|
+
### `cache().data`
|
|
790
|
+
|
|
791
|
+
Upstream veri önbelleği (`withDataCache`). Ayrıntı: [06-cache.md](./06-cache.md).
|
|
792
|
+
|
|
793
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
794
|
+
| --- | --- | --- | --- |
|
|
795
|
+
| `maxEntries` | `number` | `10000` | LRU girdi sınırı. JSON, HTML'e göre onlarca kat küçük olduğu için sınır yüksek. **Tavan 20000**; üstü uyarıyla kesilir. Süreç içi JSON ayrıca **64 MB**'yi geçemez; bu bütçe config'den yükseltilmez. |
|
|
796
|
+
| `staleFactor` | `number` | `10` | TTL dolduktan sonra girdinin kaç TTL boyunca daha kullanılabileceği. `0` → bayat servis yok. |
|
|
797
|
+
|
|
798
|
+
### `cache().trackUpstream`
|
|
799
|
+
|
|
800
|
+
**Tip:** `boolean` — **Varsayılan:** `true`
|
|
801
|
+
|
|
802
|
+
Açıkken `globalThis.fetch` sarılır ve render sırasındaki geçici upstream
|
|
803
|
+
hataları (`429`, `5xx`, ağ) kendiliğinden bildirilir; `reportUpstreamFailure()`
|
|
804
|
+
çağırmak gerekmez. `fetch`i kendisi saran bir uygulama bunu kapatabilir.
|
|
805
|
+
|
|
806
|
+
### `cache().trackDependencies`
|
|
807
|
+
|
|
808
|
+
**Tip:** `boolean` — **Varsayılan:** `true`
|
|
809
|
+
|
|
810
|
+
Açıkken bir render'ın okuduğu `withDataCache` anahtarları kaydedilir ve
|
|
811
|
+
`clearDataCache()` o veriyi okumuş HTML sayfalarını da bayatlatır — hedefli
|
|
812
|
+
invalidation için uygulamanın hiçbir şey bildirmesi gerekmez
|
|
813
|
+
([06-cache.md](./06-cache.md)). `withDataCache` kullanmayan bir uygulamada
|
|
814
|
+
kaydedilecek bir şey yok; kapatmak bağlam kurma maliyetini de kaldırır.
|
|
815
|
+
|
|
816
|
+
### `cache().transientRetry`
|
|
817
|
+
|
|
818
|
+
**Tip:** `{ attempts?: number, delayMs?: number } | false` —
|
|
819
|
+
**Varsayılan:** `{ attempts: 1, delayMs: 300 }`
|
|
820
|
+
|
|
821
|
+
Geçici bir upstream hatası yüzünden `notFound()` çağrılan sayfa kaç kez daha
|
|
822
|
+
denenir. Amaç var olan bir sayfanın 404'e dönüşmemesi; denemeler tükenirse yanıt
|
|
823
|
+
önbelleğe girmeyen bir 503 olur. `false` ya da `attempts: 0` tekrarı kapatır.
|
|
824
|
+
Ayrıntı: [06-cache.md](./06-cache.md).
|
|
825
|
+
|
|
826
|
+
### `cache().upstream`
|
|
827
|
+
|
|
828
|
+
Upstream API'ye giden `fetch` çağrılarının host başına hız freni. Varsayılan
|
|
829
|
+
**kapalı**: `rate` verilmedikçe hiçbir istek beklemez. `rate` bir tavandır;
|
|
830
|
+
gerçek hız 429 cevaplarına göre kendini aşağı çeker ve temiz geçen pencerelerde
|
|
831
|
+
kademe kademe geri çıkar.
|
|
832
|
+
|
|
833
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
834
|
+
| --- | --- | --- | --- |
|
|
835
|
+
| `rate` | `number` | `0` | Saniyedeki en fazla çağrı. `0` → fren kapalı |
|
|
836
|
+
| `burst` | `number` | `0` | Kova boyu; `0` → bir saniyelik bütçe kadar patlama |
|
|
837
|
+
| `concurrency` | `number` | `8` | Aynı anda uçabilecek çağrı |
|
|
838
|
+
| `minRate` | `number` | `0.5` | Azalmanın dibi; hız buranın altına inmez |
|
|
839
|
+
| `increaseStep` | `number` | `1` | Toplamsal artışın adımı (çağrı/saniye) |
|
|
840
|
+
| `increaseIntervalMs` | `number` | `5000` | Artış periyodu |
|
|
841
|
+
| `decreaseIntervalMs` | `number` | `1000` | İki azalma arasındaki en kısa süre |
|
|
842
|
+
| `breakerFailures` | `number` | `5` | Art arda kaç 429'dan sonra host baypas edilir |
|
|
843
|
+
| `breakerCooldownMs` | `number` | `10000` | Baypasın süresi |
|
|
844
|
+
| `hosts` | `Record<string, object>` | `{}` | Host bazlı override; aynı alanlar geçerli |
|
|
845
|
+
|
|
846
|
+
Yalnızca `429` ve `503` hızı cezalandırır: `400`/`404`/`500` bir kota sorunu
|
|
847
|
+
değil. Durumu `getUpstreamLimiterStatus()` ile ya da dev panelinin **Server**
|
|
848
|
+
sekmesinden okuyabilirsin. Ayrıntı ve freni açmadan önce bakılacak yer:
|
|
849
|
+
[06-cache.md](./06-cache.md).
|
|
850
|
+
|
|
851
|
+
```js
|
|
852
|
+
upstream: {
|
|
853
|
+
rate: 10,
|
|
854
|
+
concurrency: 4,
|
|
855
|
+
hosts: { "api.example.com": { rate: 3 } },
|
|
856
|
+
}
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### `cache().redis`
|
|
860
|
+
|
|
861
|
+
Opsiyonel Redis ikinci kademesi (L2). Bellek içi önbellek birincil kalır; Redis
|
|
862
|
+
yalnızca L1'de bulunmayan bir yol için render'ı atlatır ve invalidation'ı diğer
|
|
863
|
+
instance'lara yayar. `ioredis` uygulamaya kurulmalı (`npm install ioredis`);
|
|
864
|
+
kurulmadıysa ya da bağlanılamıyorsa uyarı basılır ve site bellek içi önbellekle
|
|
865
|
+
çalışmaya devam eder.
|
|
866
|
+
|
|
867
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
868
|
+
| --- | --- | --- | --- |
|
|
869
|
+
| `enabled` | `boolean` | `false` | Yalnızca açıkça `true` verildiğinde açılır |
|
|
870
|
+
| `url` | `string \| null` | `null` | `redis://` ya da `rediss://`. Boşsa ioredis varsayılanı (`localhost:6379`) |
|
|
871
|
+
| `namespace` | `string` | `"default"` | Aynı Redis'i paylaşan uygulamaları ayırır |
|
|
872
|
+
| `keyPrefix` | `string` | `"_jskelet"` | Anahtar düzeninin kökü |
|
|
873
|
+
| `html` | `boolean` | `true` | HTML gövdeleri paylaşılsın mı |
|
|
874
|
+
| `data` | `boolean` | `true` | `withDataCache` girdileri paylaşılsın mı |
|
|
875
|
+
| `storeEncoded` | `boolean` | `false` | Brotli/gzip gövdeleri de paylaşılsın mı; girdi başına boyutu iki-üç katına çıkarır |
|
|
876
|
+
| `events` | `boolean` | `true` | pub/sub üzerinden invalidation yayını |
|
|
877
|
+
| `commandTimeoutMs` | `number` | `200` | Tek bir komutun en fazla bekletebileceği süre |
|
|
878
|
+
|
|
879
|
+
Anahtarlar `_jskelet:{namespace}:{buildId}:html:{yol}?{query}` biçiminde yaşar.
|
|
880
|
+
`buildId` her build'de değişir, böylece deploy sonrası eski HTML kendiliğinden
|
|
881
|
+
geçersiz olur. Kişiye özel (`storable: false`), `degraded` ve 200 dışındaki
|
|
882
|
+
yanıtlar paylaşımlı kademeye hiç yazılmaz. Takaslar ve teşhis:
|
|
883
|
+
[06-cache.md](./06-cache.md).
|
|
884
|
+
|
|
885
|
+
```js
|
|
886
|
+
redis: {
|
|
887
|
+
enabled: process.env.NODE_ENV === "production",
|
|
888
|
+
url: process.env.REDIS_URL,
|
|
889
|
+
namespace: "haber-sitesi",
|
|
890
|
+
}
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
### `logs`
|
|
894
|
+
|
|
895
|
+
Kalıcı log sink'leri. Varsayılan her şey kapalı: stdout ve admin paneli ring'i
|
|
896
|
+
mevcut davranışını korur. Açıldığında HTTP access log ile framework olayları
|
|
897
|
+
(`event` / `error`) NDJSON olarak dosyaya, `drainLog`'a ve/veya S3'e gider.
|
|
898
|
+
Dosya parçaları zstd'dir ve en fazla 5 dakika durur; süresi dolan en eski
|
|
899
|
+
parça silinir.
|
|
900
|
+
|
|
901
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
902
|
+
| --- | --- | --- | --- |
|
|
903
|
+
| `console` | `boolean` | `true` | Runtime `http` / `event` / `error` satırları stdout'a basılsın mı (banner/build satırları etkilenmez) |
|
|
904
|
+
| `kinds` | `("http" \| "event" \| "error")[]` | hepsi | Sink'lere giden kayıt türleri |
|
|
905
|
+
| `file.enabled` | `boolean` | `false` | Dosya spool'u. Satırlar ~1 sn veya 32 satırda bir `jskelet-<zaman>-<n>.ndjson.zst` olur. En fazla 5 dakika tutulur; en eski parça silinir. |
|
|
906
|
+
| `file.dir` | `string` | `"logs"` | Proje köküne göre dizin |
|
|
907
|
+
| `drainLog` | `(chunk) => void \| Promise<void>` | `null` | Mühürlenen zstd parçasını (`{ body, encoding, bytes, lines, at }`) istenen yere aktarır. Hata uyarı basar, siteyi düşürmez. Dosya kapalıysa diske yazılmaz. |
|
|
908
|
+
| `s3.enabled` | `boolean` | `false` | S3 batch PutObject sink'i |
|
|
909
|
+
| `s3.bucket` | `string \| null` | `null` | Bucket ya da `bucket/prefix/…` yolu; `JSKELET_LOG_BUCKET` ezer |
|
|
910
|
+
| `s3.prefix` | `string` | `"jskelet/logs/"` | Nesne anahtarı öneki (yolda verilmediyse) |
|
|
911
|
+
| `s3.region` | `string \| null` | `"auto"` | Bölge; verilmezse `JSKELET_S3_REGION`, yoksa `auto` |
|
|
912
|
+
| `s3.endpoint` | `string \| null` | `null` | S3-uyumlu API adresi; `JSKELET_S3_API_URL` ezer |
|
|
913
|
+
| `s3.flushIntervalMs` | `number` | `5000` | Batch flush aralığı |
|
|
914
|
+
| `s3.maxBatch` | `number` | `100` | Bu kadar satırda erken flush |
|
|
915
|
+
|
|
916
|
+
S3 credential'ları config'e yazılmaz: `JSKELET_S3_ACCESS_KEY_ID`,
|
|
917
|
+
`JSKELET_S3_SECRET_ACCESS_KEY`, isteğe bağlı `JSKELET_S3_SESSION_TOKEN`.
|
|
918
|
+
Bucket/region/credential eksikse uyarı basılır ve S3 sink kapanır; site ayağa
|
|
919
|
+
kalkmaya devam eder. Framework `@aws-sdk` taşımaz — PutObject SigV4 ile
|
|
920
|
+
gömülüdür.
|
|
921
|
+
|
|
922
|
+
```js
|
|
923
|
+
logs: {
|
|
924
|
+
console: true,
|
|
925
|
+
kinds: ["http", "error"],
|
|
926
|
+
file: { enabled: true, dir: "logs" },
|
|
927
|
+
async drainLog(chunk) {
|
|
928
|
+
// chunk.body zstd NDJSON. Dosya 5 dakika sonra silinir; kalıcı kopya burada.
|
|
929
|
+
},
|
|
930
|
+
s3: {
|
|
931
|
+
enabled: process.env.NODE_ENV === "production",
|
|
932
|
+
bucket: process.env.JSKELET_LOG_BUCKET,
|
|
933
|
+
prefix: "my-app/logs/",
|
|
934
|
+
region: process.env.JSKELET_S3_REGION,
|
|
935
|
+
endpoint: process.env.JSKELET_S3_API_URL,
|
|
936
|
+
},
|
|
937
|
+
}
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
### `admin()`
|
|
941
|
+
|
|
942
|
+
Framework yönetim paneli (`/_jskelet/admin`). Bellek içi / Redis / Cloudflare
|
|
943
|
+
önbelleğini yönetir; route ve view envanteri ile canlı log kuyruğu sunar.
|
|
944
|
+
|
|
945
|
+
Ortama bakmaz: `enabled` verilmedikçe **hiç mount edilmez** ve yol da yoktur.
|
|
946
|
+
Açıkken production'da da çalışır — asıl sorular ("bu sayfa neden bayat",
|
|
947
|
+
"webhook purge'ü geçti mi") orada soruluyor. `cache()` bölümünden ayrıdır.
|
|
948
|
+
|
|
949
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
950
|
+
| --- | --- | --- | --- |
|
|
951
|
+
| `enabled` | `boolean` | `false` | Yalnızca açıkça `true` verildiğinde açılır (`JSKELET_ADMIN` ezer) |
|
|
952
|
+
| `basePath` | `string` | `"/_jskelet/admin"` | Panelin kökü |
|
|
953
|
+
| `allowIps` | `string[]` | `[]` | Exact IP veya CIDR; boş = kısıt yok. Listede olmayan her istek 404 |
|
|
954
|
+
| `blockBots` | `boolean` | `true` | Bilinen crawler UA'ları 404 |
|
|
955
|
+
| `banAttempts` | `number` | `3` | Kaç başarısız denemeden sonra IP yasaklanır |
|
|
956
|
+
| `banHours` | `number` | `24` | Yasağın süresi |
|
|
957
|
+
| `sessionHours` | `number` | `12` | Oturum çerezinin ömrü |
|
|
958
|
+
| `logSize` | `number` | `500` | Canlı log ring boyutu |
|
|
959
|
+
|
|
960
|
+
Şifre **her süreç başlangıcında** üretilir ve yalnızca sunucu logundaki
|
|
961
|
+
`ADMIN` kutusunda görünür. Yasaklı ve yetkisiz her cevap `404`'tür. Kullanım
|
|
962
|
+
ve ekran ayrıntıları: [06-cache.md](./06-cache.md).
|
|
963
|
+
|
|
964
|
+
```js
|
|
965
|
+
admin() {
|
|
966
|
+
return {
|
|
967
|
+
enabled: process.env.JSKELET_ADMIN === "1",
|
|
968
|
+
allowIps: ["203.0.113.10", "10.0.0.0/8"],
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
### `cache().cloudflare`
|
|
974
|
+
|
|
975
|
+
CDN kademesi. JSkelet'in önbelleği origin önbelleği; ziyaretçinin gördüğü kopya
|
|
976
|
+
edge'de duruyor. Bu bölüm bağlıysa panelden edge purge'ü, cache ile ilgili zone
|
|
977
|
+
ayarları ve cache isabet oranı yönetilebilir.
|
|
978
|
+
|
|
979
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
980
|
+
| --- | --- | --- | --- |
|
|
981
|
+
| `enabled` | `boolean` | `true` | `false` verilirse env'de token olsa bile yüzey kapalı kalır |
|
|
982
|
+
| `zoneId` | `string \| null` | `null` | Zone kimliği (`JSKELET_CLOUDFLARE_ZONE_ID` ezer) |
|
|
983
|
+
| `apiToken` | `string \| null` | `null` | Token; **env tercih edilir**, config'e yazmak sırrı repoya sokar |
|
|
984
|
+
| `hostname` | `string \| null` | `null` | Purge tam URL ister; yol → URL çevrimi bu ad üzerinden yapılır. Verilmezse panelin açıldığı origin kullanılır |
|
|
985
|
+
| `analyticsHours` | `number` | `24` | Analitik penceresi, en çok `72` |
|
|
986
|
+
|
|
987
|
+
Token yalnızca `JSKELET_CLOUDFLARE_KEY` ile verildiğinde config dosyası temiz
|
|
988
|
+
kalır; izinler yapılacak işe göre: purge için `Zone.Cache Purge`, ayarlar için
|
|
989
|
+
`Zone.Zone Settings`, isabet oranı için `Zone.Analytics` (salt okunur). Token
|
|
990
|
+
hiçbir panel cevabında dönmez, yalnızca "env'den geldi" bilgisi görünür.
|
|
991
|
+
|
|
992
|
+
Zone bağlı değilse panel bir uyarı değil kurulum önerisi gösterir; Cloudflare
|
|
993
|
+
hata dönerse ilgili bölüm hatayı yazar ve panelin kalanı çalışmaya devam eder.
|
|
994
|
+
Neyin sorulabildiği — özellikle "bu sayfa kaç edge'de cache'li" sorusunun neden
|
|
995
|
+
tam cevabı olmadığı — [06-cache.md](./06-cache.md) içinde.
|
|
996
|
+
|
|
997
|
+
### `cache().prewarm`
|
|
998
|
+
|
|
999
|
+
İki mod: **klasik** (liste + açılış turu) veya **`onVisit`** (ziyaret edilen
|
|
1000
|
+
sayfadaki linkler). Birlikte verilemez — config yüklenirken hata.
|
|
1001
|
+
|
|
1002
|
+
#### Klasik alanlar
|
|
1003
|
+
|
|
1004
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
1005
|
+
| --- | --- | --- | --- |
|
|
1006
|
+
| `enabled` | `boolean` | `true` | `false` ise ısıtma yapılmaz (`PREWARM=1` ile ezilebilir) |
|
|
1007
|
+
| `max` | `number` | `400` | Bir turda en fazla kaç yol ısıtılır |
|
|
1008
|
+
| `concurrency` | `number` | prod 4, dev 1 | Paralel işçi sayısı |
|
|
1009
|
+
| `rps` | `number` | prod `0`, dev 4 | Saniyedeki en fazla ısıtma isteği; `0` sınırsız. Upstream kotasını koruyan ayar bu. Dev'deki varsayılan fren, ısıtmanın sayfa isteklerini bekletmemesi için. |
|
|
1010
|
+
| `delayMs` | `number` | prod 500, dev 3000 | Açılıştan sonra ilk turun gecikmesi |
|
|
1011
|
+
| `retryDelayMs` | `number` | `2000` | Tekrar turundan önce beklenen süre |
|
|
1012
|
+
| `intervalSeconds` | `number` | `0` | 0'dan büyükse tur periyodik tekrarlanır |
|
|
1013
|
+
| `rotate` | `boolean` | `true` | Liste `max`'tan uzunsa periyodik turlar kaldığı yerden devam eder |
|
|
1014
|
+
| `priority` | `(string \| RegExp)[]` | `[]` | Isıtma sırası; eşleşen yollar her turda başa alınır |
|
|
1015
|
+
| `origins` | `string[]` | `[]` | Klasik turda ısıtılacak origin'ler. Boşsa `http://127.0.0.1:<port>`. `vary.host` açıksa locale host'ları buraya yazın |
|
|
1016
|
+
|
|
1017
|
+
`priority` iki biçim kabul eder: config'in her yerinde geçerli olan desen
|
|
1018
|
+
sözdizimi ve doğrudan `RegExp`. Önce yazılan önce ısınır.
|
|
1019
|
+
|
|
1020
|
+
```js
|
|
1021
|
+
prewarm: {
|
|
1022
|
+
max: 500,
|
|
1023
|
+
rps: 4,
|
|
1024
|
+
intervalSeconds: 300,
|
|
1025
|
+
// vary.host açıksa loopback tek başına yetmez:
|
|
1026
|
+
origins: ["http://localhost", "http://tr.localhost"],
|
|
1027
|
+
priority: [
|
|
1028
|
+
"/", // ana sayfa
|
|
1029
|
+
"/piyasalar/:path*", // tüm piyasa bölümü
|
|
1030
|
+
/-yorumlar$/, // desen sözdiziminin karşılamadığı kural
|
|
1031
|
+
],
|
|
1032
|
+
}
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
#### `onVisit`
|
|
1036
|
+
|
|
1037
|
+
| Alan | Tip | Varsayılan | Anlamı |
|
|
1038
|
+
| --- | --- | --- | --- |
|
|
1039
|
+
| `onVisit` | `true \| false \| object` | kapalı | Ziyaret tabanlı ısıtma |
|
|
1040
|
+
| `onVisit.perPage` | `number` | `20` | Sayfa başına üstten alta en fazla link. **Tavan 20** |
|
|
1041
|
+
| `onVisit.concurrency` | `number` | `2` | Paralel işçi. **Tavan 2** |
|
|
1042
|
+
| `onVisit.rps` | `number` | `2` | Saniyedeki tavan. **Tavan 2**; `0` da 2'ye çekilir |
|
|
1043
|
+
|
|
1044
|
+
```js
|
|
1045
|
+
prewarm: {
|
|
1046
|
+
onVisit: { perPage: 20, rps: 2 },
|
|
1047
|
+
}
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
`hooks.prewarmPaths` ve klasik alanlar (`max`, `priority`, …) `onVisit` ile
|
|
1051
|
+
**yasaktır**. Ayrıntı: [06-cache.md](./06-cache.md).
|
|
1052
|
+
|
|
1053
|
+
Sayısal alanların her biri aynı adı taşıyan ortam değişkeniyle ezilebilir; env
|
|
1054
|
+
önceliklidir. Ayrıntı: [06-cache.md](./06-cache.md).
|
|
1055
|
+
|
|
1056
|
+
## `hooks`
|
|
1057
|
+
|
|
1058
|
+
**Tip:** `Record<string, Function>` — **Varsayılan:** `{}`
|
|
1059
|
+
|
|
1060
|
+
Hepsi opsiyonel, hepsi `async` olabilir. Bir hook hata verirse framework kendi
|
|
1061
|
+
varsayılanına döner ve uyarır — sayfa düşmez.
|
|
1062
|
+
|
|
1063
|
+
| Hook | İmza | Döndürdüğü | Belge |
|
|
1064
|
+
| --- | --- | --- | --- |
|
|
1065
|
+
| `metadata` | `(page) => object` | Her sayfanın metadata varsayılanı; controller `metadata`sı üzerine biner | [04](./04-render-ve-sablonlar.md) |
|
|
1066
|
+
| `layoutContext` | `({ pathname, metadata }) => object` | Layout local'leri; `lang`, `structuredData`, `extraHead`, `bodyClass` özel yorumlanır | [04](./04-render-ve-sablonlar.md) |
|
|
1067
|
+
| `notFound` | `() => object \| null` | 404 sayfa tanımı; `null` ise framework'ün hata sayfası | [03](./03-routing.md) |
|
|
1068
|
+
| `error` | `({ status, error }) => object \| string \| null` | 404 dışındaki hata sayfaları (ve `notFound` yoksa 404); sayfa tanımı ya da doğrudan HTML | [03](./03-routing.md) |
|
|
1069
|
+
| `prewarmPaths` | `() => string[]` | Klasik ısıtmada ısıtılacak yollar; tanımlı değilse klasik tur kurulmaz. `onVisit` ile birlikte **yasak** | [06](./06-cache.md) |
|
|
1070
|
+
|
|
1071
|
+
```js
|
|
1072
|
+
hooks: {
|
|
1073
|
+
metadata() {
|
|
1074
|
+
return { titleTemplate: "%s | Örnek", siteUrl: "https://ornek.com" };
|
|
1075
|
+
},
|
|
1076
|
+
|
|
1077
|
+
async layoutContext({ pathname }) {
|
|
1078
|
+
return { navigation: await getNavigation(), isHome: pathname === "/" };
|
|
1079
|
+
},
|
|
1080
|
+
|
|
1081
|
+
notFound() {
|
|
1082
|
+
return {
|
|
1083
|
+
view: "pages/not-found",
|
|
1084
|
+
metadata: { title: "Sayfa bulunamadı", robots: { index: false } },
|
|
1085
|
+
};
|
|
1086
|
+
},
|
|
1087
|
+
|
|
1088
|
+
error({ status }) {
|
|
1089
|
+
return {
|
|
1090
|
+
view: "pages/error",
|
|
1091
|
+
data: { status },
|
|
1092
|
+
metadata: { title: "Bir hata oluştu", robots: { index: false } },
|
|
1093
|
+
};
|
|
1094
|
+
},
|
|
1095
|
+
|
|
1096
|
+
async prewarmPaths() {
|
|
1097
|
+
return ["/", ...(await getArticlePaths())];
|
|
1098
|
+
},
|
|
1099
|
+
}
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
## `source` desen sözdizimi
|
|
1103
|
+
|
|
1104
|
+
`headers()`, `redirects()`, `rewrites()` ve `cache().html` aynı küçük derleyiciyi
|
|
1105
|
+
kullanır. Bu, Next'in tam `path-to-regexp` yüzeyi değil; config'te fiilen
|
|
1106
|
+
kullanılan alt küme bilinçli olarak seçildi ve tanınmayan bir sözdizimi sessizce
|
|
1107
|
+
literal kabul edilmez, uyarı üretir.
|
|
1108
|
+
|
|
1109
|
+
| Desen | Regex karşılığı | Örnek eşleşme |
|
|
1110
|
+
| --- | --- | --- |
|
|
1111
|
+
| `/hakkinda` | tam eşleşme | `/hakkinda` |
|
|
1112
|
+
| `/haber/:slug` | `([^/]+)` — tek segment | `/haber/abc` (✗ `/haber/a/b`) |
|
|
1113
|
+
| `/:path*` | `(.*)` — sıfır veya daha fazla segment | `/`, `/a`, `/a/b/c` |
|
|
1114
|
+
| `/blog/:path*` | joker alt yol; öndeki `/` opsiyonel | `/blog`, `/blog/`, `/blog/a/b` |
|
|
1115
|
+
| `/:path*.svg` | joker + sabit son ek | `/ikon.svg`, `/a/b/c.svg` |
|
|
1116
|
+
| `/etiket-:slug` | segment ortasında parametre | `/etiket-finans` |
|
|
1117
|
+
|
|
1118
|
+
Kurallar:
|
|
1119
|
+
|
|
1120
|
+
- `source` **`/` ile başlamak zorundadır**; başlamazsa kural yok sayılır ve
|
|
1121
|
+
uyarı basılır.
|
|
1122
|
+
- Parametre adı `[A-Za-z_][A-Za-z0-9_]*` kalıbına uymalıdır.
|
|
1123
|
+
- Desen daima **baştan sona** eşleşir (`^…$`); önek eşleşmesi için `:path*`
|
|
1124
|
+
kullanın.
|
|
1125
|
+
- `:path*` sıfır segment de yakalar ve hemen öncesindeki `/` opsiyoneldir:
|
|
1126
|
+
`/hesabim/:path*` bölümün kök yolunu (`/hesabim`) da kapsar. Aksi hâlde bir
|
|
1127
|
+
bölümü tamamen kapatmak isteyen kural tam da giriş sayfasını atlıyordu.
|
|
1128
|
+
- Parametreler dışındaki tüm karakterler literal kabul edilir ve regex için
|
|
1129
|
+
kaçışlanır — `.` gerçekten nokta demektir.
|
|
1130
|
+
- Yakalanan değerler `destination` içindeki aynı adlı `:param`'lara yazılır.
|
|
1131
|
+
Karşılığı olmayan bir yer tutucu olduğu gibi bırakılır.
|
|
1132
|
+
|
|
1133
|
+
## Ortam değişkenleri
|
|
1134
|
+
|
|
1135
|
+
Framework'ün okuduğu tüm değişkenler. `.env` dosyası varsa CLI tarafından
|
|
1136
|
+
otomatik yüklenir (`--env-file=.env`); yoksa bayrak hiç geçilmez ve uyarı
|
|
1137
|
+
basılmaz.
|
|
1138
|
+
|
|
1139
|
+
| Değişken | Kim okur | Varsayılan | Anlamı |
|
|
1140
|
+
| --- | --- | --- | --- |
|
|
1141
|
+
| `NODE_ENV` | her yer | `production` (start/build), `development` (dev) | Dev overlay, EJS cache, manifest yeniden okuma, route hata davranışı ve prewarm varsayılanlarını belirler. `jskelet dev` bunu kendisi ayarlar — `cross-env` gerekmez. |
|
|
1142
|
+
| `PORT` | `startServer` | `3000` | Dinlenecek port. Doluysa süreç başlamaz; `jskelet start|dev --murder` dinleyiciyi öldürür |
|
|
1143
|
+
| `HOST` | `startServer` | `::` | Bağlanılacak arayüz. Varsayılan çift yığın dinler (IPv6 + IPv4); IPv6 yoksa `0.0.0.0`'a düşer |
|
|
1144
|
+
| `JSKELET_SECRET` | `jskelet/cookies` | — | İmzalı cookie sırrı. `security.cookieSecret` verilmediğinde buradan okunur; ikisi de yoksa imzalı cookie API'si hata verir. [12](./12-panel-ve-oturum.md) |
|
|
1145
|
+
| `DEV_GATE` | `devGate` | kapalı | `1` gate'i açar, `0` config'te açık olsa da kapatır. `DEV_TOKEN` tek başına açmaz. [09](./09-dev-araclari.md) |
|
|
1146
|
+
| `DEV_TOKEN` | `devGate`, `prewarm` | — | Gate açıkken beklenen sır. Yoksa veya gate kapalıysa site açık kalır. Isıtma, gate açıkken token'ı çerez olarak taşır. [09](./09-dev-araclari.md) |
|
|
1147
|
+
| `JSKELET_ADMIN` | `createApp` | — | Ayarlıysa yönetim panelini açar; `0` config'te açık olan paneli kapatır. Env config'i ezer, çünkü panel genelde bir arıza sırasında tek seferlik açılır. [06](./06-cache.md) |
|
|
1148
|
+
| `JSKELET_LOG_BUCKET` | `logs.s3` | — | Log hedefi: bucket ya da `bucket/prefix` yolu. Credential ile birlikte varsa sink otomatik açılır |
|
|
1149
|
+
| `JSKELET_S3_BUCKET` | `logs.s3` | — | `JSKELET_LOG_BUCKET` yoksa bucket; `JSKELET_S3_KEY_PREFIX` ile birleşir |
|
|
1150
|
+
| `JSKELET_S3_KEY_PREFIX` | `logs.s3` | — | `JSKELET_S3_BUCKET` ile kullanılır (`bucket/prefix`) |
|
|
1151
|
+
| `JSKELET_S3_ACCESS_KEY_ID` | `logs.s3` | — | PutObject imzası |
|
|
1152
|
+
| `JSKELET_S3_SECRET_ACCESS_KEY` | `logs.s3` | — | İmza sırrı (`JSKELET_S3_ACCESS_SECRET` yedek ad) |
|
|
1153
|
+
| `JSKELET_S3_SESSION_TOKEN` | `logs.s3` | — | Geçici credential için isteğe bağlı |
|
|
1154
|
+
| `JSKELET_S3_REGION` | `logs.s3` | `auto` | Verilmezse `auto` |
|
|
1155
|
+
| `JSKELET_S3_API_URL` | `logs.s3` | — | S3-uyumlu endpoint; `logs.s3.endpoint`'i ezer |
|
|
1156
|
+
| `JSKELET_CLOUDFLARE_KEY` | Cloudflare cache yüzeyi | — | API token. Verilene kadar CDN purge'ü ve edge analitiği kapalıdır; config'teki `apiToken`'ı ezer. Token hiçbir cevapta dönmez. [06](./06-cache.md) |
|
|
1157
|
+
| `JSKELET_CLOUDFLARE_ZONE_ID` | Cloudflare cache yüzeyi | — | Zone kimliği. Token'la birlikte verilmedikçe hiçbir Cloudflare ucu çağrılmaz |
|
|
1158
|
+
| `JSKELET_CLOUDFLARE_HOSTNAME` | Cloudflare cache yüzeyi | — | Purge URL'lerinin kökü. Panel iç bir adresten açılıyorsa gerekir |
|
|
1159
|
+
| `PREWARM` | `startPrewarm` | — | `0` ısıtmayı kapatır; `1` config'teki `enabled: false`'u ezip açar |
|
|
1160
|
+
| `PREWARM_MAX` | `prewarm` | `400` | En fazla kaç yol ısıtılır |
|
|
1161
|
+
| `PREWARM_CONCURRENCY` | `prewarm` | prod 4, dev 1 | Paralel işçi sayısı |
|
|
1162
|
+
| `PREWARM_RPS` | `prewarm` | `0` | Saniyedeki en fazla ısıtma isteği; `0` sınırsız |
|
|
1163
|
+
| `PREWARM_DELAY_MS` | `startPrewarm` | prod 500, dev 3000 | İlk turun gecikmesi |
|
|
1164
|
+
| `PREWARM_RETRY_DELAY_MS` | `prewarm` | `2000` | Tekrar turundan önceki bekleme |
|
|
1165
|
+
| `PREWARM_INTERVAL_SECONDS` | `startPrewarm` | `0` | 0'dan büyükse periyodik tur |
|
|
1166
|
+
| `JSKELET_VERBOSE` | `jskelet dev` | — | `1` ise restart'ta değişen dosyaların tamamı listelenir |
|
|
1167
|
+
| `JSKELET_COLOR` | `jskelet/log` | — | `1` ise renk zorlanır. Alt süreçler boruya yazdığı için renk algılaması kapanır; `jskelet dev` bunu kendisi ayarlar. |
|
|
1168
|
+
| `JSKELET_CHILD` | `jskelet build` | — | Dev script'i tarafından ayarlanır; build banner'ı ve "Ready" özetini bastırır |
|
|
1169
|
+
| `NO_COLOR` | `jskelet/log` | — | Ayarlıysa renk hiç kullanılmaz (`JSKELET_COLOR`u da ezer) |
|
|
1170
|
+
|
|
1171
|
+
Uygulamanızın kendi değişkenleri (API origin'i, token'lar) framework tarafından
|
|
1172
|
+
okunmaz; doğrudan `process.env` üzerinden kullanın. Tarayıcıya ulaşması
|
|
1173
|
+
gerekenleri `clientEnv` ile bildirin.
|
|
1174
|
+
|
|
1175
|
+
Sayısal prewarm ayarları yalnızca **pozitif ve sonlu** değer kabul eder;
|
|
1176
|
+
geçersiz bir değer sessizce bir sonraki katmana (config → kod varsayılanı)
|
|
1177
|
+
düşer.
|
|
1178
|
+
|
|
1179
|
+
## Programatik erişim
|
|
1180
|
+
|
|
1181
|
+
```js
|
|
1182
|
+
import { getConfig, loadConfig } from "jskelet";
|
|
1183
|
+
|
|
1184
|
+
await loadConfig(); // proje kökünden okur
|
|
1185
|
+
await loadConfig({ root: "/baska/proje" }); // farklı kök
|
|
1186
|
+
await loadConfig({ configFile: "jskelet.test.mjs" });
|
|
1187
|
+
await loadConfig({ force: true }); // önbelleği atlayıp yeniden oku
|
|
1188
|
+
|
|
1189
|
+
const config = getConfig(); // çözümlenmiş config
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
`loadConfig()` aynı süreçte ikinci çağrıda önbelleğe düşer: `jskelet start` hem
|
|
1193
|
+
`ensure-build` hem `createApp` üzerinden çağırıyor ve config'i iki kez okuyup iki
|
|
1194
|
+
kez loglamanın faydası yok.
|
|
1195
|
+
|
|
1196
|
+
`getConfig()` `loadConfig()` çağrılmadan kullanılırsa **hata verir**: sessiz
|
|
1197
|
+
yanlış yol, "stylesheet neden yok" gibi teşhisi zor sorunlara dönüşüyor.
|
|
1198
|
+
|
|
1199
|
+
Çözümlenmiş config'te dizinler mutlak yol olarak `config.dirs` altındadır
|
|
1200
|
+
(`views`, `public`, `client`, `routes`, `styles`, `generated`, `assets`,
|
|
1201
|
+
`fonts`), desenler derlenmiş hâldedir ve `config.loaded` dosyanın gerçekten
|
|
1202
|
+
okunup okunmadığını söyler.
|
|
1203
|
+
|
|
1204
|
+
## Sırada ne var
|
|
1205
|
+
|
|
1206
|
+
- Build tarafındaki alanların etkisi: [08-build.md](./08-build.md)
|
|
1207
|
+
- Dev akışı ve `DEV_TOKEN`: [09-dev-araclari.md](./09-dev-araclari.md)
|
|
1208
|
+
- Ortam değişkenlerinin dağıtımda kullanımı: [10-dagitim.md](./10-dagitim.md)
|