@pylonsync/functions 0.3.300 → 0.3.301

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/ssr-runtime.ts +56 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pylonsync/functions",
3
- "version": "0.3.300",
3
+ "version": "0.3.301",
4
4
  "description": "TypeScript function runtime for pylon — defines server-side queries, mutations, and actions.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -2485,6 +2485,27 @@ export async function handleRenderRoute(
2485
2485
  wantsStream,
2486
2486
  status: responseState.status,
2487
2487
  });
2488
+ // Dev diagnostics (the agent + HUD signal): the cache verdict + the single
2489
+ // actionable reason, computed ONCE here and reused for the dev HUD blob, the
2490
+ // `x-pylon-dev` header (→ the host's diagnostics ring + `pylon diagnostics`),
2491
+ // and the structured dev log. Null in prod.
2492
+ const renderMode = wantsStream ? "ssr-streaming" : "ssr-buffered";
2493
+ const devVerdict = isDevMode()
2494
+ ? describeCacheVerdict({
2495
+ bucketOptIn,
2496
+ cacheable,
2497
+ bucketable,
2498
+ revalidateSecs,
2499
+ authTouched,
2500
+ dynamicTouched,
2501
+ sessionTouched,
2502
+ cookieCount: responseState.cookies.length,
2503
+ strictPolicies,
2504
+ wantsStream,
2505
+ forceDynamic,
2506
+ status: responseState.status,
2507
+ })
2508
+ : null;
2488
2509
  // Serialization view of props built from the RAW `msg` values — the live
2489
2510
  // `props` (with its read-tracking proxies) is NEVER mutated back to raw, so
2490
2511
  // it can't become a vehicle that leaks a prior request's identity to a later
@@ -2522,19 +2543,32 @@ export async function handleRenderRoute(
2522
2543
  type: "response_start",
2523
2544
  call_id: msg.call_id,
2524
2545
  status: responseState.status,
2525
- headers: finalizeHeaders(
2526
- responseState,
2527
- undefined,
2546
+ headers: finalizeHeaders(responseState, undefined, {
2528
2547
  // The #277 anon proof and the Phase 0 bucket proof both ride the TRUSTED
2529
- // `internal` channel (never stripped), so userland (page setHeader /
2530
- // route-handler headers via `extra`) can't forge either. Mutually
2531
- // exclusive (bucketOptIn splits the two verdicts).
2532
- bucketable
2548
+ // `internal` channel (never stripped here, always stripped by the host
2549
+ // before the client), so userland (page setHeader / route-handler headers
2550
+ // via `extra`) can't forge either. Mutually exclusive (bucketOptIn splits
2551
+ // the two verdicts).
2552
+ ...(bucketable
2533
2553
  ? { "x-pylon-bucket": String(revalidateSecs) }
2534
2554
  : cacheable
2535
2555
  ? { "x-pylon-cacheable": String(revalidateSecs) }
2536
- : undefined,
2537
- ),
2556
+ : {}),
2557
+ // Dev-only: the host parses this into its diagnostics ring (served at
2558
+ // /_pylon/dev/diagnostics + `pylon diagnostics`). Single-line JSON, no
2559
+ // newlines. Stripped before the client like every x-pylon-* header.
2560
+ ...(devVerdict
2561
+ ? {
2562
+ "x-pylon-dev": JSON.stringify({
2563
+ verdict: devVerdict.verdict,
2564
+ secs: devVerdict.secs,
2565
+ reason: devVerdict.reason,
2566
+ mode: renderMode,
2567
+ component: msg.component,
2568
+ }),
2569
+ }
2570
+ : {}),
2571
+ }),
2538
2572
  });
2539
2573
 
2540
2574
  // Pre-load the manifest BEFORE the React stream starts emitting
@@ -2692,32 +2726,25 @@ export async function handleRenderRoute(
2692
2726
  }
2693
2727
 
2694
2728
  // Dev HUD (dev only): append the cache-verdict + render-timing blob + the
2695
- // floating overlay. After the page tail so its marker/probe are in place
2696
- // before the deferred client entry boots the sync engine.
2697
- if (isDevMode()) {
2698
- const verdict = describeCacheVerdict({
2699
- bucketOptIn,
2700
- cacheable,
2701
- bucketable,
2702
- revalidateSecs,
2703
- authTouched,
2704
- dynamicTouched,
2705
- sessionTouched,
2706
- cookieCount: responseState.cookies.length,
2707
- strictPolicies,
2708
- wantsStream,
2709
- forceDynamic,
2710
- status: responseState.status,
2711
- });
2729
+ // floating overlay, and emit ONE structured log line. After the page tail so
2730
+ // the HUD marker/probe are in place before the deferred client entry boots the
2731
+ // sync engine. `devVerdict` was computed once above (reused here + in the
2732
+ // x-pylon-dev header). The log rides the runtime's inherited stderr, so an
2733
+ // agent running `pylon dev` sees the verdict without any extra call.
2734
+ if (devVerdict) {
2735
+ const renderMs = Math.round((performance.now() - renderStart) * 10) / 10;
2712
2736
  sendChunk(
2713
2737
  buildDevHudChunk({
2714
2738
  route: msg.url,
2715
2739
  component: msg.component,
2716
- renderMode: wantsStream ? "ssr-streaming" : "ssr-buffered",
2717
- renderMs: Math.round((performance.now() - renderStart) * 10) / 10,
2718
- cache: verdict,
2740
+ renderMode,
2741
+ renderMs,
2742
+ cache: devVerdict,
2719
2743
  }),
2720
2744
  );
2745
+ // The structured dev-log line is emitted host-side (Rust tracing) from the
2746
+ // x-pylon-dev header, so it rides the same log stream as every other
2747
+ // [pylon] line — no duplicate console.error here.
2721
2748
  }
2722
2749
 
2723
2750
  send({ type: "render_done", call_id: msg.call_id });