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 CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.850",
3
+ "version": "0.1.851",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -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+0B3B,CAAC"}
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('page-data', path, startedAt, { source: timingSource, status: response.status });
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('page-data', path, startedAt, { source: timingSource, status: response.status });
474
+ emitRouteTiming(
475
+ 'page-data',
476
+ path,
477
+ startedAt,
478
+ buildPageDataTimingDetail(response, endpoint, startedAt, timingSource)
479
+ );
336
480
  }
337
481
 
338
482
  if (triggerReloadOnVersionMismatch) {
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.850";
2
+ export declare const VERSION = "0.1.851";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.850";
4
+ export const VERSION = "0.1.851";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.850",
3
+ "version": "0.1.851",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",