veryfront 0.1.850 → 0.1.851
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/esm/deno.js +1 -1
- package/esm/src/html/hydration-script-builder/templates/router.d.ts.map +1 -1
- package/esm/src/html/hydration-script-builder/templates/router.js +146 -2
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
package/esm/deno.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/router.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,cA+
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/router.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,cA+9B3B,CAAC"}
|
|
@@ -37,6 +37,7 @@ export const getRouterScript = () => `
|
|
|
37
37
|
const PREFETCH_DELAY_MS = 100;
|
|
38
38
|
const MAX_PREFETCH_PATHS = 100;
|
|
39
39
|
const MAX_ROUTE_TIMINGS = 100;
|
|
40
|
+
const MAX_SERVER_TIMING_LENGTH = 1024;
|
|
40
41
|
|
|
41
42
|
// ============================================
|
|
42
43
|
// Debug logging (production-safe)
|
|
@@ -159,6 +160,139 @@ export const getRouterScript = () => `
|
|
|
159
160
|
return entry;
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
function sanitizeServerTimingHeader(value) {
|
|
164
|
+
if (!value) return null;
|
|
165
|
+
|
|
166
|
+
const metrics = [];
|
|
167
|
+
const printable = String(value).replace(/[^\\x20-\\x7E]/g, ' ').trim();
|
|
168
|
+
if (!printable) return null;
|
|
169
|
+
|
|
170
|
+
for (const item of printable.split(',')) {
|
|
171
|
+
const segments = item.split(';').map((segment) => segment.trim()).filter(Boolean);
|
|
172
|
+
const name = sanitizeServerTimingMetricName(segments[0]);
|
|
173
|
+
if (!name) continue;
|
|
174
|
+
|
|
175
|
+
for (const segment of segments.slice(1)) {
|
|
176
|
+
const [key, rawValue = ''] = segment.split('=');
|
|
177
|
+
if (key.trim().toLowerCase() !== 'dur') continue;
|
|
178
|
+
|
|
179
|
+
const duration = Number(rawValue.trim().replace(/^"|"$/g, ''));
|
|
180
|
+
if (!Number.isFinite(duration) || duration < 0) continue;
|
|
181
|
+
|
|
182
|
+
metrics.push(name + ';dur=' + (Math.round(duration * 100) / 100).toFixed(2));
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const sanitized = metrics.join(', ');
|
|
188
|
+
return sanitized ? sanitized.slice(0, MAX_SERVER_TIMING_LENGTH) : null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function sanitizeServerTimingMetricName(name) {
|
|
192
|
+
return String(name || '').trim().replace(/[^A-Za-z0-9_.-]/g, '_').slice(0, 128);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function parseServerTimingMetrics(value) {
|
|
196
|
+
const header = sanitizeServerTimingHeader(value);
|
|
197
|
+
if (!header) return null;
|
|
198
|
+
|
|
199
|
+
const metrics = {};
|
|
200
|
+
for (const item of header.split(',')) {
|
|
201
|
+
const segments = item.split(';').map((segment) => segment.trim()).filter(Boolean);
|
|
202
|
+
const name = sanitizeServerTimingMetricName(segments[0]);
|
|
203
|
+
if (!name) continue;
|
|
204
|
+
|
|
205
|
+
for (const segment of segments.slice(1)) {
|
|
206
|
+
const [key, rawValue = ''] = segment.split('=');
|
|
207
|
+
if (key.trim().toLowerCase() !== 'dur') continue;
|
|
208
|
+
|
|
209
|
+
const duration = Number(rawValue.trim().replace(/^"|"$/g, ''));
|
|
210
|
+
if (Number.isFinite(duration) && duration >= 0) {
|
|
211
|
+
metrics[name] = Math.round(duration * 100) / 100;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return Object.keys(metrics).length ? metrics : null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function readResponseServerTiming(response) {
|
|
220
|
+
try {
|
|
221
|
+
return sanitizeServerTimingHeader(response.headers?.get('server-timing'));
|
|
222
|
+
} catch (_) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function roundRouteTimingValue(value) {
|
|
228
|
+
return Math.round(value * 100) / 100;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function extractResourceTiming(entry) {
|
|
232
|
+
const fields = [
|
|
233
|
+
'startTime',
|
|
234
|
+
'requestStart',
|
|
235
|
+
'responseStart',
|
|
236
|
+
'responseEnd',
|
|
237
|
+
'duration',
|
|
238
|
+
'transferSize',
|
|
239
|
+
'encodedBodySize',
|
|
240
|
+
'decodedBodySize'
|
|
241
|
+
];
|
|
242
|
+
const timing = {};
|
|
243
|
+
|
|
244
|
+
for (const field of fields) {
|
|
245
|
+
const value = entry?.[field];
|
|
246
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
|
|
247
|
+
timing[field] = roundRouteTimingValue(value);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return Object.keys(timing).length ? timing : null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function getPageDataResourceTiming(endpoint, fetchStartedAt) {
|
|
255
|
+
try {
|
|
256
|
+
if (typeof performance === 'undefined' || typeof performance.getEntriesByName !== 'function') {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const href = new URL(endpoint, window.location.href).href;
|
|
261
|
+
const entries = performance.getEntriesByName(href, 'resource');
|
|
262
|
+
if (!entries.length) return null;
|
|
263
|
+
|
|
264
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
265
|
+
const entry = entries[index];
|
|
266
|
+
if (
|
|
267
|
+
typeof entry?.responseEnd === 'number' &&
|
|
268
|
+
Number.isFinite(entry.responseEnd) &&
|
|
269
|
+
entry.responseEnd + 1 >= fetchStartedAt
|
|
270
|
+
) {
|
|
271
|
+
return extractResourceTiming(entry);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return null;
|
|
276
|
+
} catch (_) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function buildPageDataTimingDetail(response, endpoint, fetchStartedAt, source) {
|
|
282
|
+
const detail = { source, status: response.status };
|
|
283
|
+
const serverTiming = readResponseServerTiming(response);
|
|
284
|
+
if (serverTiming) {
|
|
285
|
+
detail.serverTiming = serverTiming;
|
|
286
|
+
const serverTimingMetrics = parseServerTimingMetrics(serverTiming);
|
|
287
|
+
if (serverTimingMetrics) detail.serverTimingMetrics = serverTimingMetrics;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const resourceTiming = getPageDataResourceTiming(response.url || endpoint, fetchStartedAt);
|
|
291
|
+
if (resourceTiming) detail.resourceTiming = resourceTiming;
|
|
292
|
+
|
|
293
|
+
return detail;
|
|
294
|
+
}
|
|
295
|
+
|
|
162
296
|
// ============================================
|
|
163
297
|
// LRU Cache with TTL (single Map to prevent sync issues)
|
|
164
298
|
// ============================================
|
|
@@ -320,7 +454,12 @@ export const getRouterScript = () => `
|
|
|
320
454
|
if (!response.ok) {
|
|
321
455
|
perfEnd('fetch:' + path);
|
|
322
456
|
if (recordRouteTiming) {
|
|
323
|
-
emitRouteTiming(
|
|
457
|
+
emitRouteTiming(
|
|
458
|
+
'page-data',
|
|
459
|
+
path,
|
|
460
|
+
startedAt,
|
|
461
|
+
buildPageDataTimingDetail(response, endpoint, startedAt, timingSource)
|
|
462
|
+
);
|
|
324
463
|
}
|
|
325
464
|
const error = new Error('Failed to fetch page data: ' + response.status);
|
|
326
465
|
error.status = response.status;
|
|
@@ -332,7 +471,12 @@ export const getRouterScript = () => `
|
|
|
332
471
|
perfEnd('parse:' + path);
|
|
333
472
|
perfEnd('fetch:' + path);
|
|
334
473
|
if (recordRouteTiming) {
|
|
335
|
-
emitRouteTiming(
|
|
474
|
+
emitRouteTiming(
|
|
475
|
+
'page-data',
|
|
476
|
+
path,
|
|
477
|
+
startedAt,
|
|
478
|
+
buildPageDataTimingDetail(response, endpoint, startedAt, timingSource)
|
|
479
|
+
);
|
|
336
480
|
}
|
|
337
481
|
|
|
338
482
|
if (triggerReloadOnVersionMismatch) {
|