jskelet 0.2.2 → 0.2.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 +375 -315
- package/docs/03-routing.md +4 -0
- package/docs/06-cache.md +154 -10
- package/docs/07-yapilandirma.md +63 -2
- package/docs/en/03-routing.md +4 -0
- package/docs/en/06-caching.md +154 -11
- package/docs/en/07-configuration.md +65 -2
- package/package.json +1 -1
- package/src/client/cache-panel/i18n.js +670 -0
- package/src/client/cache-panel/login.html +23 -12
- package/src/client/cache-panel/panel.css +314 -6
- package/src/client/cache-panel/panel.html +205 -30
- package/src/client/cache-panel/panel.js +605 -29
- package/src/config/defaults.js +31 -0
- package/src/config/index.js +87 -2
- package/src/index.js +16 -1
- package/src/log.mjs +25 -0
- package/src/server/cache-panel.js +294 -40
- package/src/server/cloudflare.js +595 -0
- package/src/server/redis.js +108 -0
- package/src/server/render.js +74 -2
- package/src/version.mjs +9 -0
package/src/server/render.js
CHANGED
|
@@ -207,8 +207,13 @@ export function route(controller, options = {}) {
|
|
|
207
207
|
const revalidate = isPrivate
|
|
208
208
|
? undefined
|
|
209
209
|
: resolveRevalidate(req.path, options.revalidate);
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
// Anahtar `null` ise query bu yol için cache'lenebilir değil: sayfa
|
|
211
|
+
// dinamik davranır. Anahtar yine de gerekiyor (hata sayfası ölçümü,
|
|
212
|
+
// teşhis) ama TTL sıfırlanıp cache yolu kapatılır.
|
|
213
|
+
const key = buildCacheKey(req.path, ctx.query);
|
|
214
|
+
const cacheable =
|
|
215
|
+
!isPrivate && req.method === "GET" && Boolean(revalidate) && key !== null;
|
|
216
|
+
const cacheKey = key ?? `${req.path}?${new URLSearchParams(
|
|
212
217
|
Object.entries(ctx.query).map(([k, v]) => [k, String(v)]),
|
|
213
218
|
).toString()}`;
|
|
214
219
|
|
|
@@ -409,6 +414,73 @@ const revalidateByPath = new Map();
|
|
|
409
414
|
*/
|
|
410
415
|
const REVALIDATE_CACHE_MAX = 2000;
|
|
411
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Aynı gerekçeyle (yakalayıcı route'ta sınırsız büyüme) query kuralı da yol
|
|
419
|
+
* başına hatırlanır.
|
|
420
|
+
*
|
|
421
|
+
* @type {Map<string, true | string[] | null>}
|
|
422
|
+
*/
|
|
423
|
+
const queryPolicyByPath = new Map();
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* @param {string} pathname
|
|
427
|
+
* @returns {true | string[] | null} `null`: bu yol için hiçbir parametreye
|
|
428
|
+
* izin verilmiyor.
|
|
429
|
+
*/
|
|
430
|
+
function resolveQueryPolicy(pathname) {
|
|
431
|
+
if (queryPolicyByPath.has(pathname)) {
|
|
432
|
+
return queryPolicyByPath.get(pathname) ?? null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const rules = getConfig().cacheQuery;
|
|
436
|
+
const match = rules.find((rule) => matchPattern(rule.pattern, pathname));
|
|
437
|
+
|
|
438
|
+
if (queryPolicyByPath.size >= REVALIDATE_CACHE_MAX) {
|
|
439
|
+
const oldest = queryPolicyByPath.keys().next().value;
|
|
440
|
+
if (oldest !== undefined) queryPolicyByPath.delete(oldest);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const policy = match ? match.allow : null;
|
|
444
|
+
queryPolicyByPath.set(pathname, policy);
|
|
445
|
+
return policy;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* HTML cache anahtarı, ya da query bu yol için cache'lenebilir değilse `null`.
|
|
450
|
+
*
|
|
451
|
+
* Varsayılan olarak query parametresi taşıyan istek dinamiktir: `cache().query`
|
|
452
|
+
* altında eşleşen bir kural olmadıkça cache'e hiç girmez. Aksi hâlde bir yolun
|
|
453
|
+
* bütün `?utm_source=…` varyantları ayrı girdi olur ve LRU'daki gerçek
|
|
454
|
+
* sayfaları dışarı atar.
|
|
455
|
+
*
|
|
456
|
+
* İzin verilen parametreler **sıralı** yazılır: `?a=1&b=2` ile `?b=2&a=1` aynı
|
|
457
|
+
* sayfa olduğu için aynı anahtarı almalı.
|
|
458
|
+
*
|
|
459
|
+
* @param {string} pathname
|
|
460
|
+
* @param {Record<string, unknown>} query
|
|
461
|
+
* @returns {string | null}
|
|
462
|
+
*/
|
|
463
|
+
function buildCacheKey(pathname, query) {
|
|
464
|
+
const entries = Object.entries(query);
|
|
465
|
+
if (!entries.length) return `${pathname}?`;
|
|
466
|
+
|
|
467
|
+
const policy = resolveQueryPolicy(pathname);
|
|
468
|
+
if (policy === null) return null;
|
|
469
|
+
|
|
470
|
+
const kept =
|
|
471
|
+
policy === true ? entries : entries.filter(([name]) => policy.includes(name));
|
|
472
|
+
|
|
473
|
+
// İzin listesi dışındaki parametreler anahtara girmez: sayfa cache'lenir ve
|
|
474
|
+
// bütün kampanya varyantları tek kopyayı paylaşır.
|
|
475
|
+
const params = new URLSearchParams(
|
|
476
|
+
kept
|
|
477
|
+
.map(/** @returns {[string, string]} */ ([name, value]) => [name, String(value)])
|
|
478
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
return `${pathname}?${params.toString()}`;
|
|
482
|
+
}
|
|
483
|
+
|
|
412
484
|
/**
|
|
413
485
|
* @param {string} pathname
|
|
414
486
|
* @param {number | undefined} fallback
|
package/src/version.mjs
CHANGED
|
@@ -20,3 +20,12 @@ export const FRAMEWORK_VERSION = manifest.version ?? "0.0.0";
|
|
|
20
20
|
|
|
21
21
|
/** Paket adı: sürüm kontrolü hangi kayıt defteri girdisine bakacağını buradan bilir. */
|
|
22
22
|
export const FRAMEWORK_PACKAGE = manifest.name ?? "jskelet";
|
|
23
|
+
|
|
24
|
+
/** Künye alanları: yönetim panelinin footer'ı bunları basıyor. */
|
|
25
|
+
export const FRAMEWORK_LICENSE = manifest.license ?? "MIT";
|
|
26
|
+
|
|
27
|
+
/** @type {string} */
|
|
28
|
+
export const FRAMEWORK_NODE_RANGE = manifest.engines?.node ?? "";
|
|
29
|
+
|
|
30
|
+
/** @type {string} */
|
|
31
|
+
export const FRAMEWORK_HOMEPAGE = manifest.homepage ?? "";
|