jskelet 0.7.2 → 0.7.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.
@@ -324,28 +324,57 @@ export function toKebab(name) {
324
324
  * build yokken 404 link'i istememek için. `.jsk` ifade dilinde `asset()` /
325
325
  * `hasAsset()` çağrılamadığı için layout bu etiketi kullanır.
326
326
  *
327
- * @param {{ styles?: string[] }} [props]
327
+ * `blocking` varsayılan `true`: `<link rel="stylesheet">` boyamayı bekletir.
328
+ * Ölçümde bu, sheet geç gelince oluşan CLS'ten daha ucuz. `false` yalnızca
329
+ * açık boolean'dır (`:blocking="false"`); atlamak veya `true` aynı çıktıyı
330
+ * verir.
331
+ *
332
+ * @param {{ styles?: string[], blocking?: boolean }} [props]
328
333
  * @returns {string}
329
334
  */
330
335
  export function stylesheets(props = {}) {
331
336
  const styles = Array.isArray(props.styles) ? props.styles : [];
337
+ const blocking = props.blocking !== false;
332
338
  /** @type {string[]} */
333
339
  const parts = [];
334
340
 
335
- if (hasAsset("app.css")) {
336
- parts.push(
337
- `<link rel="stylesheet" href="${esc(asset("app.css"))}" data-jskelet-css="app.css">`,
338
- );
339
- }
341
+ pushStylesheet(parts, "app.css", blocking);
340
342
 
341
343
  for (const sheet of styles) {
342
- if (!sheet || !hasAsset(sheet)) continue;
344
+ if (!sheet) continue;
345
+ pushStylesheet(parts, sheet, blocking);
346
+ }
347
+
348
+ return parts.join("\n ");
349
+ }
350
+
351
+ /**
352
+ * Tek bir manifest anahtarı için stylesheet linki.
353
+ *
354
+ * `blocking: false` iken `media="print"`: eşleşmeyen media tarayıcıda
355
+ * render-blocking sayılmaz, indirme boyamayı bekletmez. `onload` sheet
356
+ * inince `media`'yı `all` yapar. JS kapalıyken `onload` çalışmaz ve link
357
+ * print'te kalır; `<noscript>` içindeki normal link o yolu kapatır.
358
+ *
359
+ * @param {string[]} parts
360
+ * @param {string} name Manifest anahtarı (`app.css`, `home.css`, …)
361
+ * @param {boolean} blocking
362
+ */
363
+ function pushStylesheet(parts, name, blocking) {
364
+ if (!hasAsset(name)) return;
365
+
366
+ const href = esc(asset(name));
367
+ const id = esc(name);
368
+ if (blocking) {
343
369
  parts.push(
344
- `<link rel="stylesheet" href="${esc(asset(sheet))}" data-jskelet-css="${esc(sheet)}">`,
370
+ `<link rel="stylesheet" href="${href}" data-jskelet-css="${id}">`,
345
371
  );
372
+ return;
346
373
  }
347
374
 
348
- return parts.join("\n ");
375
+ parts.push(
376
+ `<link rel="stylesheet" href="${href}" media="print" onload="this.media='all'" data-jskelet-css="${id}">\n <noscript><link rel="stylesheet" href="${href}" data-jskelet-css="${id}"></noscript>`,
377
+ );
349
378
  }
350
379
 
351
380
  /**
@@ -260,11 +260,19 @@ export declare const DEFAULT_REDIS: {
260
260
  /** pub/sub üzerinden invalidation yayını. */
261
261
  events: boolean;
262
262
  /**
263
- * Tek bir komutun en fazla bekletebileceği süre. Önbellek okuması isteği
263
+ * Veri, yayın ve SCAN için tek komut tavanı. Önbellek okuması isteği
264
264
  * bloklayan bir adım: Redis takıldığında render'a düşmek, ağı beklemekten
265
- * iyidir.
265
+ * iyidir. HTML gövdesi bu süreye sığmaz; o bağlantının tavanı
266
+ * `htmlCommandTimeoutMs`.
266
267
  */
267
268
  commandTimeoutMs: number;
269
+ /**
270
+ * Yalnızca HTML bağlantısı. ioredis `commandTimeout` istemci genelinde
271
+ * olduğu için gövde ayrı bağlantıda durur. Verilmezse 2000: uygulama
272
+ * config'i değiştirmek zorunda kalmasın. Parçalı yazma normalde bunun
273
+ * altında biter; tavan, gövde beklenmedik büyürse diye durur.
274
+ */
275
+ htmlCommandTimeoutMs: number;
268
276
  };
269
277
  /**
270
278
  * Kalıcı log sink'leri (dosya + S3).
@@ -34,7 +34,14 @@ export type RedisConfig = {
34
34
  * pub/sub invalidation yayını.
35
35
  */
36
36
  events: boolean;
37
+ /**
38
+ * Veri, yayın ve SCAN tavanı.
39
+ */
37
40
  commandTimeoutMs: number;
41
+ /**
42
+ * Yalnızca HTML bağlantısının tavanı.
43
+ */
44
+ htmlCommandTimeoutMs: number;
38
45
  };
39
46
  export type LogKind = "http" | "event" | "error";
40
47
  export type LogsConfig = {
@@ -210,6 +210,8 @@ export declare function buildReport(devtools: {
210
210
  buildId: string;
211
211
  errors: number;
212
212
  bypassed: boolean;
213
+ htmlErrors: number;
214
+ htmlBypassed: boolean;
213
215
  };
214
216
  };
215
217
  upstream: {
@@ -48,6 +48,40 @@ export declare function redisSharesEncoded(): boolean;
48
48
  * @returns {string}
49
49
  */
50
50
  export declare function cacheKey(kind: "html" | "data", key: string): string;
51
+ /**
52
+ * HTML kaydını parçalı yazar. Meta küçük JSON'dur; gövde ham brotli baytıdır
53
+ * (base64 yok, JSON içinde ikinci brotli yok). `encoded` varsa her kodlama
54
+ * `:enc:` anahtarına olduğu gibi gider.
55
+ *
56
+ * Ateşle-unut ve tek sayaç adımı: üç SET'ten biri düşerse bu bir hatadır,
57
+ * veri devre kesicisine yazılmaz.
58
+ *
59
+ * @param {string} key Önbellek anahtarı (`yol?query`), öneksiz.
60
+ * @param {Record<string, unknown>} meta
61
+ * @param {Buffer} body
62
+ * @param {Map<string, Buffer> | null} encoded
63
+ * @param {number} ttlMs
64
+ */
65
+ export declare function redisShareHtml(key: string, meta: Record<string, unknown>, body: Buffer, encoded: Map<string, Buffer> | null, ttlMs: number): void;
66
+ /**
67
+ * Meta + gövde (+ açıksa kodlamalar) tek pipeline. Parça eksikse kayıt yok
68
+ * sayılır; bu bir hata değildir. Bağlantı hatası HTML sayacına yazılır.
69
+ *
70
+ * @param {string} key Önbellek anahtarı (`yol?query`), öneksiz.
71
+ * @returns {Promise<{ html: string, status: unknown, storedAt: unknown,
72
+ * expiresAt: unknown, staleUntil: unknown, produceMs: unknown, deps: unknown,
73
+ * encoded: Map<string, Buffer> } | null>}
74
+ */
75
+ export declare function redisGetHtml(key: string): Promise<{
76
+ html: string;
77
+ status: unknown;
78
+ storedAt: unknown;
79
+ expiresAt: unknown;
80
+ staleUntil: unknown;
81
+ produceMs: unknown;
82
+ deps: unknown;
83
+ encoded: Map<string, Buffer>;
84
+ } | null>;
51
85
  /**
52
86
  * @param {string} key
53
87
  * @returns {Promise<any | null>} Girdi yoksa, ayrıştırılamıyorsa ya da Redis
@@ -85,7 +119,8 @@ export declare function redisDropMatching(kind: "html" | "data", match?: (key: s
85
119
  * Dev raporu için durum özeti. Bağlantı yoksa da güvenle çağrılabilir.
86
120
  *
87
121
  * @returns {{ enabled: boolean, connected: boolean, keyPrefix: string,
88
- * buildId: string, errors: number, bypassed: boolean }}
122
+ * buildId: string, errors: number, bypassed: boolean, htmlErrors: number,
123
+ * htmlBypassed: boolean }}
89
124
  */
90
125
  export declare function getRedisStatus(): {
91
126
  enabled: boolean;
@@ -94,6 +129,8 @@ export declare function getRedisStatus(): {
94
129
  buildId: string;
95
130
  errors: number;
96
131
  bypassed: boolean;
132
+ htmlErrors: number;
133
+ htmlBypassed: boolean;
97
134
  };
98
135
  /**
99
136
  * Bağlantının **nereye** kurulduğu ve hangi ayarlarla çalıştığı.
@@ -106,7 +143,7 @@ export declare function getRedisStatus(): {
106
143
  * @returns {{ address: string, secure: boolean, db: string | null,
107
144
  * namespace: string, keyPrefix: string, html: boolean, data: boolean,
108
145
  * storeEncoded: boolean, events: boolean, commandTimeoutMs: number,
109
- * subscribed: boolean }}
146
+ * htmlCommandTimeoutMs: number, subscribed: boolean }}
110
147
  */
111
148
  export declare function getRedisDetails(): {
112
149
  address: string;
@@ -119,6 +156,7 @@ export declare function getRedisDetails(): {
119
156
  storeEncoded: boolean;
120
157
  events: boolean;
121
158
  commandTimeoutMs: number;
159
+ htmlCommandTimeoutMs: number;
122
160
  subscribed: boolean;
123
161
  };
124
162
  /**
@@ -152,8 +190,10 @@ export declare function disconnectRedis(): Promise<void>;
152
190
  *
153
191
  * @param {any} fake `null` → katman kapatılır.
154
192
  * @param {Partial<RedisConfig>} [overrides]
193
+ * @param {any} [htmlFake] Verilmezse `fake` hem veri hem HTML istemcisidir.
194
+ * HTML zaman aşımını veri sayacından ayırmak için ayrı bir sahte verilir.
155
195
  */
156
- export declare function setRedisClientForTests(fake: any, overrides?: Partial<RedisConfig>): void;
196
+ export declare function setRedisClientForTests(fake: any, overrides?: Partial<RedisConfig>, htmlFake?: any): void;
157
197
  /**
158
198
  * Testler için: abone kanalından gelmiş gibi olay besler. `originId`
159
199
  * verilmezse uzak bir node varsayılır; kendi kimliğini taşıyan bir yayını
@@ -102,11 +102,17 @@ export declare function toKebab(name: string): string;
102
102
  * build yokken 404 link'i istememek için. `.jsk` ifade dilinde `asset()` /
103
103
  * `hasAsset()` çağrılamadığı için layout bu etiketi kullanır.
104
104
  *
105
- * @param {{ styles?: string[] }} [props]
105
+ * `blocking` varsayılan `true`: `<link rel="stylesheet">` boyamayı bekletir.
106
+ * Ölçümde bu, sheet geç gelince oluşan CLS'ten daha ucuz. `false` yalnızca
107
+ * açık boolean'dır (`:blocking="false"`); atlamak veya `true` aynı çıktıyı
108
+ * verir.
109
+ *
110
+ * @param {{ styles?: string[], blocking?: boolean }} [props]
106
111
  * @returns {string}
107
112
  */
108
113
  export declare function stylesheets(props?: {
109
114
  styles?: string[];
115
+ blocking?: boolean;
110
116
  }): string;
111
117
  /**
112
118
  * Layout gövde sonu script'leri: `main.js`, controller `entries`, isteğe