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.
@@ -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
- const cacheable = !isPrivate && req.method === "GET" && Boolean(revalidate);
211
- const cacheKey = `${req.path}?${new URLSearchParams(
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 ?? "";