@timber-js/app 0.2.0-alpha.126 → 0.2.0-alpha.128

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.
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/plugins/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA0B1D,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAElF;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA8KtD"}
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/plugins/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA0B1D,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAElF;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAsLtD"}
@@ -48,8 +48,11 @@ export declare function buildCodecChainType(p: ParamEntry, importBase: string |
48
48
  * schema's InferCodecType. Falls back to the param's base type when
49
49
  * schema doesn't declare this param (schemas are opt-in per key).
50
50
  *
51
- * Generates a conditional that checks key existence:
52
- * `[bracketName] extends keyof typeof schema.segmentParams ? InferCodecType<...> : fallback`
51
+ * Uses `infer` to extract the codec value rather than direct indexing
52
+ * because TypeScript doesn't narrow index validity inside a conditional
53
+ * type's true branch — `typeof schema.segmentParams[K]` produces TS2538
54
+ * even when the condition proves K is a valid key. In .d.ts files that
55
+ * error is suppressed and the type silently becomes `any`.
53
56
  */
54
57
  export declare function buildSchemaParamType(p: ParamEntry, fallback?: string): string;
55
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"codegen-shared.d.ts","sourceRoot":"","sources":["../../src/routing/codegen-shared.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAM7F;AAED,sEAAsE;AACtE,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAE5E;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,CAOtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,UAAU,EACb,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,CAaR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrF"}
1
+ {"version":3,"file":"codegen-shared.d.ts","sourceRoot":"","sources":["../../src/routing/codegen-shared.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAM7F;AAED,sEAAsE;AACtE,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAE5E;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,CAOtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,UAAU,EACb,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,CAaR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAOrF"}
@@ -1,4 +1,4 @@
1
1
  import { i as INTERCEPTION_MARKERS, n as classifyUrlSegment, r as DEFAULT_PAGE_EXTENSIONS, t as classifySegment } from "../_chunks/segment-classify-BjfuctV2.js";
2
2
  import { m as scanRoutes } from "../_chunks/cli-schema-sync-DFHBj6yZ.js";
3
- import { a as generateRouteMap, i as collectInterceptionRewrites, n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-DMSGZeCk.js";
3
+ import { a as generateRouteMap, i as collectInterceptionRewrites, n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-E5ss0qES.js";
4
4
  export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, classifyUrlSegment, collectDynamicSegmentsFromTree, collectInterceptionRewrites, collectLeafRoutes, generateRouteMap, scanRoutes, validateSchemaAgainstRoutes };
@@ -17,7 +17,7 @@
17
17
  import { DenySignal } from './primitives.js';
18
18
  import type { ManifestSegmentNode } from './route-matcher.js';
19
19
  import type { RouteMatch } from './pipeline.js';
20
- import type { NavContext } from './ssr-entry.js';
20
+ import type { NavContext } from './ssr-bridge-types.js';
21
21
  import type { ClientBootstrapConfig } from './html-injectors.js';
22
22
  /** Layout component entry for deny page wrapping. */
23
23
  export interface LayoutEntry {
@@ -1 +1 @@
1
- {"version":3,"file":"deny-renderer.d.ts","sourceRoot":"","sources":["../../src/server/deny-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAO7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAOjE,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;IAC3C,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,iEAAiE;AACjE,MAAM,MAAM,mBAAmB,GAAG,MAAM;IACtC,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,SAAS,GAAG,CACtB,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,KACnB,OAAO,CAAC,QAAQ,CAAC,CAAC;AAUvB;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,EACtC,sBAAsB,EAAE,mBAAmB,EAC3C,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,QAAQ,CAAC,CAiGnB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,eAAe,EAAE,OAAO,EACxB,sBAAsB,EAAE,mBAAmB,GAC1C,OAAO,CAAC,QAAQ,CAAC,CA2CnB"}
1
+ {"version":3,"file":"deny-renderer.d.ts","sourceRoot":"","sources":["../../src/server/deny-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAO7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAOjE,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;IAC3C,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,iEAAiE;AACjE,MAAM,MAAM,mBAAmB,GAAG,MAAM;IACtC,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,SAAS,GAAG,CACtB,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,KACnB,OAAO,CAAC,QAAQ,CAAC,CAAC;AAUvB;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,EACtC,sBAAsB,EAAE,mBAAmB,EAC3C,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,QAAQ,CAAC,CAiGnB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,eAAe,EAAE,OAAO,EACxB,sBAAsB,EAAE,mBAAmB,GAC1C,OAAO,CAAC,QAAQ,CAAC,CA2CnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"error-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/error-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAYvD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqHD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,EACtC,WAAW,CAAC,EAAE,eAAe,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAwCnB;AAqID;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,GACrC,OAAO,CAAC,QAAQ,CAAC,CA6BnB"}
1
+ {"version":3,"file":"error-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/error-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAYvD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqHD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,EACtC,WAAW,CAAC,EAAE,eAAe,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAwCnB;AAqID;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,GACrC,OAAO,CAAC,QAAQ,CAAC,CAuCnB"}
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * SSR Bridge — loads the SSR entry and passes the RSC stream for HTML rendering.
3
+ *
4
+ * This is the RSC → SSR environment hop. The two environments are separate
5
+ * Vite module graphs; `NavContext` is the serializable contract that crosses
6
+ * the boundary. See `../ssr-bridge-types.ts` for the contract definition.
3
7
  */
4
- import type { NavContext } from '../ssr-entry.js';
8
+ import type { NavContext } from '../ssr-bridge-types.js';
5
9
  export declare function callSsr(rscStream: ReadableStream<Uint8Array>, navContext: NavContext): Promise<Response>;
6
10
  //# sourceMappingURL=ssr-bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-bridge.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-bridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,wBAAsB,OAAO,CAC3B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,CAenB"}
1
+ {"version":3,"file":"ssr-bridge.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,wBAAsB,OAAO,CAC3B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,CAenB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAW/D,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAarD;;;;;;;;GAQG;AACH,eAAO,MAAM,gCAAgC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAiB,CAAC;AAEhF,UAAU,gBAAgB;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;IACzC,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,qBAAqB,CAAC;IACvC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiSjF"}
1
+ {"version":3,"file":"ssr-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAIlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAW/D,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAarD;;;;;;;;GAQG;AACH,eAAO,MAAM,gCAAgC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAiB,CAAC;AAEhF,UAAU,gBAAgB;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;IACzC,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,qBAAqB,CAAC;IACvC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqSjF"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * RSC ↔ SSR Bridge Contract
3
+ *
4
+ * This file defines the payload that crosses the RSC → SSR environment
5
+ * boundary. The RSC environment calls `handleSsr(rscStream, navContext)`
6
+ * in the SSR environment via `import.meta.viteRsc.import(...)` (see
7
+ * `rsc-entry/ssr-bridge.ts`). RSC and SSR are separate Vite module graphs
8
+ * with separate module instances, so every per-request field that SSR
9
+ * needs must be declared here and passed explicitly.
10
+ *
11
+ * Rules for changes to this file:
12
+ *
13
+ * 1. **Structurally serializable shape.** Fields must be values that can
14
+ * cross the environment boundary as plain data or as platform-neutral
15
+ * primitives (`Headers`, `ReadableStream`, `AbortSignal`, `Map`). Do
16
+ * not add closures, class instances tied to RSC-only modules, or
17
+ * anything whose identity must be preserved across the bridge.
18
+ *
19
+ * 2. **No runtime imports of RSC-only modules.** This file is read by
20
+ * both environments at type-check time. Keep it type-only. Importing
21
+ * an RSC-only runtime module here couples SSR to RSC internals.
22
+ *
23
+ * 3. **Shape changes are public-surface changes.** Adding, removing, or
24
+ * renaming a field is a change to the contract between two
25
+ * independently-built bundles. Update every caller in the same PR —
26
+ * do not rely on back-compat shims.
27
+ *
28
+ * See `design/02-rendering-pipeline.md` §"RSC → SSR → Client Hydration"
29
+ * and `CLAUDE.md` §"RSC and SSR Are Separate Vite Environments".
30
+ */
31
+ /**
32
+ * Navigation context passed from the RSC environment to SSR.
33
+ *
34
+ * Per-request state must be explicitly passed across the RSC→SSR
35
+ * environment boundary since they are separate Vite module graphs.
36
+ */
37
+ export interface NavContext {
38
+ /** The requested pathname */
39
+ pathname: string;
40
+ /** Extracted route segment params (catch-all segments produce string[]) */
41
+ segmentParams: Record<string, string | string[]>;
42
+ /** Search params from the URL */
43
+ searchParams: Record<string, string>;
44
+ /** The committed HTTP status code */
45
+ statusCode: number;
46
+ /** Response headers from middleware/proxy */
47
+ responseHeaders: Headers;
48
+ /** Pre-rendered metadata HTML to inject before </head> */
49
+ headHtml: string;
50
+ /** Inline JS for React's bootstrapScriptContent — kicks off module loading */
51
+ bootstrapScriptContent: string;
52
+ /** Tee'd RSC stream for client-side hydration (inlined into HTML) */
53
+ rscStream?: ReadableStream<Uint8Array>;
54
+ /** Max Suspense hold window (ms). SSR delays the first flush by this
55
+ * duration, racing allReady so that fast-resolving boundaries render inline
56
+ * without ever showing a fallback. Derived from route `deferSuspenseFor` exports.
57
+ * See design/05-streaming.md §"deferSuspenseFor". */
58
+ deferSuspenseFor?: number;
59
+ /** Request abort signal. When the client disconnects (page refresh,
60
+ * navigation away), this signal fires. Passed to renderToReadableStream
61
+ * so React stops rendering and doesn't fire error boundaries for aborts. */
62
+ signal?: AbortSignal;
63
+ /** Request cookies as name→value pairs. Used by useCookie() during SSR
64
+ * to return correct cookie values before hydration. */
65
+ cookies?: Map<string, string>;
66
+ /**
67
+ * Mutable flag: set by TimberErrorBoundary during SSR when it catches
68
+ * a DenySignal (via digest). This tells the RSC entry that the denial
69
+ * was contained by a slot error boundary and should NOT be promoted
70
+ * to a page-level deny. See LOCAL-298.
71
+ */
72
+ _denyHandledByBoundary?: boolean;
73
+ /**
74
+ * Mutable: SSR timing data populated by handleSsr().
75
+ * Read by the RSC entry to record sub-phase Server-Timing entries
76
+ * when `serverTiming: 'detailed'` is configured.
77
+ *
78
+ * This bridges the RSC→SSR environment boundary: the SSR entry populates
79
+ * these fields, the RSC entry reads them after callSsr() returns.
80
+ */
81
+ _ssrTimings?: {
82
+ /** Time to decode RSC stream (createFromReadableStream/createFromNodeStream) */
83
+ decodeMs: number;
84
+ /** Time for Fizz to render the shell (onShellReady) */
85
+ shellMs: number;
86
+ /** Time for pipe() to flush shell bytes to the output stream */
87
+ pipeMs: number;
88
+ /** Time to set up Node.js Transform pipeline / Web Stream transforms */
89
+ pipelineMs: number;
90
+ /** Total SSR time (decode → response ready) */
91
+ totalMs: number;
92
+ /** Whether the Node.js native stream path was used */
93
+ nodeStreams: boolean;
94
+ };
95
+ /**
96
+ * Signal that fires when an unhandled RSC error is detected.
97
+ * The Flight injector checks this before writing each script tag —
98
+ * when aborted, it stops injecting Flight data to prevent corrupted
99
+ * chunks from crashing the client's Flight decoder.
100
+ */
101
+ _errorSignal?: AbortSignal;
102
+ /**
103
+ * Mutable flag: set by the SSR entry when Fizz's `stream.allReady`
104
+ * settled before the `deferSuspenseFor` hold ended.
105
+ *
106
+ * When true, the RSC entry can skip its signal-promotion
107
+ * microtask-retry wait: Fizz allReady implies Flight closed its
108
+ * stream, which implies `onError` already fired for every rejected
109
+ * Flight task. Any DenySignal/RedirectSignal/RenderError in-tree has
110
+ * already been captured in `RenderSignals`.
111
+ *
112
+ * When false or undefined (hold timed out, no hold window, shell-only
113
+ * return, Node error path), no such guarantee — the RSC entry falls
114
+ * back to the bounded microtask retry loop.
115
+ *
116
+ * See design/05-streaming.md §"deferSuspenseFor and the Hold Window"
117
+ * and design/spike-signal-promotion-protocol.md (Option 1).
118
+ */
119
+ _fizzAllReadyResolved?: boolean;
120
+ }
121
+ //# sourceMappingURL=ssr-bridge-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-bridge-types.d.ts","sourceRoot":"","sources":["../../src/server/ssr-bridge-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACjD,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,eAAe,EAAE,OAAO,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACvC;;;0DAGsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;4DACwD;IACxD,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE;QACZ,gFAAgF;QAChF,QAAQ,EAAE,MAAM,CAAC;QACjB,uDAAuD;QACvD,OAAO,EAAE,MAAM,CAAC;QAChB,gEAAgE;QAChE,MAAM,EAAE,MAAM,CAAC;QACf,wEAAwE;QACxE,UAAU,EAAE,MAAM,CAAC;QACnB,+CAA+C;QAC/C,OAAO,EAAE,MAAM,CAAC;QAChB,sDAAsD;QACtD,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IAEF;;;;;OAKG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAE3B;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC"}
@@ -12,78 +12,7 @@
12
12
  * Design docs: 18-build-system.md §"Entry Files", 02-rendering-pipeline.md
13
13
  */
14
14
  import 'virtual:timber-search-params-registry';
15
- /**
16
- * Navigation context passed from the RSC environment to SSR.
17
- *
18
- * Per-request state must be explicitly passed across the RSC→SSR
19
- * environment boundary since they are separate Vite module graphs.
20
- */
21
- export interface NavContext {
22
- /** The requested pathname */
23
- pathname: string;
24
- /** Extracted route params (catch-all segments produce string[]) */
25
- params: Record<string, string | string[]>;
26
- /** Search params from the URL */
27
- searchParams: Record<string, string>;
28
- /** The committed HTTP status code */
29
- statusCode: number;
30
- /** Response headers from middleware/proxy */
31
- responseHeaders: Headers;
32
- /** Pre-rendered metadata HTML to inject before </head> */
33
- headHtml: string;
34
- /** Inline JS for React's bootstrapScriptContent — kicks off module loading */
35
- bootstrapScriptContent: string;
36
- /** Tee'd RSC stream for client-side hydration (inlined into HTML) */
37
- rscStream?: ReadableStream<Uint8Array>;
38
- /** Max Suspense hold window (ms). SSR delays the first flush by this
39
- * duration, racing allReady so that fast-resolving boundaries render inline
40
- * without ever showing a fallback. Derived from route `deferSuspenseFor` exports.
41
- * See design/05-streaming.md §"deferSuspenseFor". */
42
- deferSuspenseFor?: number;
43
- /** Request abort signal. When the client disconnects (page refresh,
44
- * navigation away), this signal fires. Passed to renderToReadableStream
45
- * so React stops rendering and doesn't fire error boundaries for aborts. */
46
- signal?: AbortSignal;
47
- /** Request cookies as name→value pairs. Used by useCookie() during SSR
48
- * to return correct cookie values before hydration. */
49
- cookies?: Map<string, string>;
50
- /**
51
- * Mutable flag: set by TimberErrorBoundary during SSR when it catches
52
- * a DenySignal (via digest). This tells the RSC entry that the denial
53
- * was contained by a slot error boundary and should NOT be promoted
54
- * to a page-level deny. See LOCAL-298.
55
- */
56
- _denyHandledByBoundary?: boolean;
57
- /**
58
- * Mutable: SSR timing data populated by handleSsr().
59
- * Read by the RSC entry to record sub-phase Server-Timing entries
60
- * when `serverTiming: 'detailed'` is configured.
61
- *
62
- * This bridges the RSC→SSR environment boundary: the SSR entry populates
63
- * these fields, the RSC entry reads them after callSsr() returns.
64
- */
65
- _ssrTimings?: {
66
- /** Time to decode RSC stream (createFromReadableStream/createFromNodeStream) */
67
- decodeMs: number;
68
- /** Time for Fizz to render the shell (onShellReady) */
69
- shellMs: number;
70
- /** Time for pipe() to flush shell bytes to the output stream */
71
- pipeMs: number;
72
- /** Time to set up Node.js Transform pipeline / Web Stream transforms */
73
- pipelineMs: number;
74
- /** Total SSR time (decode → response ready) */
75
- totalMs: number;
76
- /** Whether the Node.js native stream path was used */
77
- nodeStreams: boolean;
78
- };
79
- /**
80
- * Signal that fires when an unhandled RSC error is detected.
81
- * The Flight injector checks this before writing each script tag —
82
- * when aborted, it stops injecting Flight data to prevent corrupted
83
- * chunks from crashing the client's Flight decoder.
84
- */
85
- _errorSignal?: AbortSignal;
86
- }
15
+ import type { NavContext } from './ssr-bridge-types.js';
87
16
  /**
88
17
  * Handle SSR: decode an RSC stream and render it to hydration-ready HTML.
89
18
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-entry.d.ts","sourceRoot":"","sources":["../../src/server/ssr-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,uCAAuC,CAAC;AAiF/C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,eAAe,EAAE,OAAO,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACvC;;;0DAGsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;4DACwD;IACxD,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE;QACZ,gFAAgF;QAChF,QAAQ,EAAE,MAAM,CAAC;QACjB,uDAAuD;QACvD,OAAO,EAAE,MAAM,CAAC;QAChB,gEAAgE;QAChE,MAAM,EAAE,MAAM,CAAC;QACf,wEAAwE;QACxE,UAAU,EAAE,MAAM,CAAC;QACnB,+CAA+C;QAC/C,OAAO,EAAE,MAAM,CAAC;QAChB,sDAAsD;QACtD,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IAEF;;;;;OAKG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,CA6LnB;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"ssr-entry.d.ts","sourceRoot":"","sources":["../../src/server/ssr-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,uCAAuC,CAAC;AA4B/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAsDxD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,CAmMnB;AAED,eAAe,SAAS,CAAC"}
@@ -39,6 +39,14 @@ export declare function renderSsrStream(element: ReactNode, options?: {
39
39
  deferSuspenseFor?: number;
40
40
  signal?: AbortSignal;
41
41
  renderTimeoutMs?: number;
42
+ /**
43
+ * Fired synchronously when the render function is about to resolve
44
+ * (i.e. at hold-end / shell-ready). `resolved` reports whether Fizz's
45
+ * `stream.allReady` had already settled by that moment. Callers use
46
+ * this to decide whether signal promotion can be short-circuited.
47
+ * See NavContext._fizzAllReadyResolved.
48
+ */
49
+ onFizzAllReadyResolved?: (resolved: boolean) => void;
42
50
  }): Promise<ReadableStream<Uint8Array>>;
43
51
  /** Whether the current platform uses native Node.js streams for SSR. */
44
52
  export declare const useNodeStreams: boolean;
@@ -54,6 +62,13 @@ export declare function renderSsrNodeStream(element: ReactNode, options?: {
54
62
  deferSuspenseFor?: number;
55
63
  signal?: AbortSignal;
56
64
  renderTimeoutMs?: number;
65
+ /**
66
+ * Fired synchronously at the moment `renderSsrNodeStream`'s returned
67
+ * Promise is about to resolve. `resolved` reports whether Fizz's
68
+ * `onAllReady` had already fired by then. See the twin callback on
69
+ * `renderSsrStream`.
70
+ */
71
+ onFizzAllReadyResolved?: (resolved: boolean) => void;
57
72
  }): Promise<import('node:stream').Readable>;
58
73
  /** Convert a Node.js Readable to a Web ReadableStream (zero-copy bridge). */
59
74
  export declare function nodeReadableToWeb(readable: import('node:stream').Readable): ReadableStream<Uint8Array>;
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-render.d.ts","sourceRoot":"","sources":["../../src/server/ssr-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAuFvC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IACR,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAErC;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,SAAkB,CAAC;AAU9C;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IACR,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,OAAO,aAAa,EAAE,QAAQ,CAAC,CAyFzC;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,aAAa,EAAE,QAAQ,GACvC,cAAc,CAAC,UAAU,CAAC,CAE5B;AAuFD;;;;;;;;;;;;GAYG;AACH,2CAA2C;AAC3C,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,UAAU,CAAC,CA2B5B;AAWD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,OAAO,GACvB,QAAQ,CASV"}
1
+ {"version":3,"file":"ssr-render.d.ts","sourceRoot":"","sources":["../../src/server/ssr-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAuFvC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IACR,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACtD,GACA,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAErC;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,SAAkB,CAAC;AAU9C;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IACR,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACtD,GACA,OAAO,CAAC,OAAO,aAAa,EAAE,QAAQ,CAAC,CAgGzC;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,aAAa,EAAE,QAAQ,GACvC,cAAc,CAAC,UAAU,CAAC,CAE5B;AAqGD;;;;;;;;;;;;GAYG;AACH,2CAA2C;AAC3C,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,UAAU,CAAC,CA2B5B;AAWD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,OAAO,GACvB,QAAQ,CASV"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.126",
3
+ "version": "0.2.0-alpha.128",
4
4
  "description": "Vite-native React framework built for Servers and Serverless Platforms — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -98,6 +98,7 @@ export function timberFonts(ctx: PluginContext): Plugin {
98
98
  resolveId(id: string) {
99
99
  let cleanId = id.startsWith('\0') ? id.slice(1) : id;
100
100
  const qIdx = cleanId.indexOf('?');
101
+ const query = qIdx !== -1 ? cleanId.slice(qIdx) : '';
101
102
  if (qIdx !== -1) cleanId = cleanId.slice(0, qIdx);
102
103
  if (cleanId.startsWith(ctx.root)) {
103
104
  const stripped = cleanId.slice(ctx.root.length);
@@ -110,7 +111,14 @@ export function timberFonts(ctx: PluginContext): Plugin {
110
111
 
111
112
  if (cleanId === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
112
113
  if (cleanId === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
113
- if (pipeline.cssIdMap.matches(cleanId)) return '\0' + cleanId;
114
+ // Preserve query suffix (notably `?direct`) on the resolved CSS id.
115
+ // Vite's transform middleware appends `?direct` when the browser
116
+ // fetches the URL as a `<link rel="stylesheet">`, and the CSS plugin
117
+ // only skips its JS-wrapping step when `isDirectCSSRequest(id)` sees
118
+ // that query on the resolved id. Dropping it here caused virtual
119
+ // font CSS to be served as Vite's HMR JS shim, leaving the
120
+ // `<link>`-loaded stylesheet empty (TIM-937 follow-up).
121
+ if (pipeline.cssIdMap.matches(cleanId)) return '\0' + cleanId + query;
114
122
  return null;
115
123
  },
116
124
 
@@ -85,13 +85,16 @@ export function buildCodecChainType(
85
85
  * schema's InferCodecType. Falls back to the param's base type when
86
86
  * schema doesn't declare this param (schemas are opt-in per key).
87
87
  *
88
- * Generates a conditional that checks key existence:
89
- * `[bracketName] extends keyof typeof schema.segmentParams ? InferCodecType<...> : fallback`
88
+ * Uses `infer` to extract the codec value rather than direct indexing
89
+ * because TypeScript doesn't narrow index validity inside a conditional
90
+ * type's true branch — `typeof schema.segmentParams[K]` produces TS2538
91
+ * even when the condition proves K is a valid key. In .d.ts files that
92
+ * error is suppressed and the type silently becomes `any`.
90
93
  */
91
94
  export function buildSchemaParamType(p: ParamEntry, fallback?: string): string {
92
95
  const key = JSON.stringify(p.bracketName);
93
96
  const fb = fallback ?? p.type;
94
- return `${key} extends keyof typeof schema['segmentParams'] ? InferCodecType<typeof schema.segmentParams[${key}]> : ${fb}`;
97
+ return `typeof schema['segmentParams'] extends Record<${key}, infer V> ? InferCodecType<V> : ${fb}`;
95
98
  }
96
99
 
97
100
  /**
@@ -27,7 +27,7 @@ import { resolveMetadata, renderMetadataToElements } from './metadata.js';
27
27
  import { resolveStatusFile } from './status-code-resolver.js';
28
28
  import type { ManifestSegmentNode } from './route-matcher.js';
29
29
  import type { RouteMatch } from './pipeline.js';
30
- import type { NavContext } from './ssr-entry.js';
30
+ import type { NavContext } from './ssr-bridge-types.js';
31
31
  import { flightInitScript } from './flight-scripts.js';
32
32
  import type { ClientBootstrapConfig } from './html-injectors.js';
33
33
  import type { Metadata } from './types.js';
@@ -166,7 +166,7 @@ export async function renderDenyPage(
166
166
 
167
167
  const navContext: NavContext = {
168
168
  pathname: new URL(req.url).pathname,
169
- params: match.segmentParams,
169
+ segmentParams: match.segmentParams,
170
170
  searchParams: Object.fromEntries(new URL(req.url).searchParams),
171
171
  statusCode: deny.status,
172
172
  responseHeaders,
@@ -21,10 +21,10 @@ import type { ManifestSegmentNode } from '../route-matcher.js';
21
21
  import { DenySignal, RenderError } from '../primitives.js';
22
22
  import type { ClientBootstrapConfig } from '../html-injectors.js';
23
23
  import { flightInitScript } from '../flight-scripts.js';
24
- import { renderDenyPage } from '../deny-renderer.js';
24
+ import { renderDenyPage, renderDenyPageAsRsc } from '../deny-renderer.js';
25
25
  import type { LayoutEntry } from '../deny-renderer.js';
26
- import type { NavContext } from '../ssr-entry.js';
27
- import { createDebugChannelSink } from './helpers.js';
26
+ import type { NavContext } from '../ssr-bridge-types.js';
27
+ import { createDebugChannelSink, isRscPayloadRequest } from './helpers.js';
28
28
  import { getCookiesForSsr } from '../cookie-context.js';
29
29
  import { callSsr } from './ssr-bridge.js';
30
30
  import { teeWithErrorPropagation } from '../stream-utils.js';
@@ -288,7 +288,7 @@ async function renderErrorPageViaRsc(
288
288
 
289
289
  const navContext: NavContext = {
290
290
  pathname: url.pathname,
291
- params: match.segmentParams,
291
+ segmentParams: match.segmentParams,
292
292
  searchParams: Object.fromEntries(url.searchParams),
293
293
  statusCode: status,
294
294
  responseHeaders,
@@ -379,6 +379,16 @@ export async function renderNoMatchPage(
379
379
  const deny = new DenySignal(404);
380
380
  const match: RouteMatch = { segments, segmentParams: {}, middlewareChain: [] };
381
381
 
382
+ if (isRscPayloadRequest(req)) {
383
+ return renderDenyPageAsRsc(
384
+ deny,
385
+ segments,
386
+ layoutComponents,
387
+ responseHeaders,
388
+ createDebugChannelSink
389
+ );
390
+ }
391
+
382
392
  return renderDenyPage(
383
393
  deny,
384
394
  segments,
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * SSR Bridge — loads the SSR entry and passes the RSC stream for HTML rendering.
3
+ *
4
+ * This is the RSC → SSR environment hop. The two environments are separate
5
+ * Vite module graphs; `NavContext` is the serializable contract that crosses
6
+ * the boundary. See `../ssr-bridge-types.ts` for the contract definition.
3
7
  */
4
8
 
5
9
  /// <reference types="@vitejs/plugin-rsc/types" />
6
10
 
7
- import type { NavContext } from '../ssr-entry.js';
11
+ import type { NavContext } from '../ssr-bridge-types.js';
8
12
 
9
13
  export async function callSsr(
10
14
  rscStream: ReadableStream<Uint8Array>,
@@ -20,7 +20,7 @@ import type { RouteMatch } from '../pipeline.js';
20
20
  import { SsrStreamError } from '../primitives.js';
21
21
  import type { LayoutComponentEntry } from '../route-element-builder.js';
22
22
  import type { ManifestSegmentNode } from '../route-matcher.js';
23
- import type { NavContext } from '../ssr-entry.js';
23
+ import type { NavContext } from '../ssr-bridge-types.js';
24
24
 
25
25
  import {
26
26
  buildRedirectResponse,
@@ -149,7 +149,7 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
149
149
 
150
150
  const navContext: NavContext = {
151
151
  pathname: new URL(req.url).pathname,
152
- params: match.segmentParams,
152
+ segmentParams: match.segmentParams,
153
153
  searchParams: Object.fromEntries(new URL(req.url).searchParams),
154
154
  statusCode: 200,
155
155
  responseHeaders,
@@ -235,33 +235,37 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
235
235
  }
236
236
 
237
237
  // Signal promotion: check if any signals were captured during rendering
238
- // inside Suspense boundaries. If no signals are present yet, yield
239
- // multiple microtasks so async component rejections propagate through
240
- // the full rejection chain to the RSC onError callback before we
241
- // commit the response.
238
+ // inside Suspense boundaries. If no signals are present yet, we may
239
+ // need to yield microtasks so async component rejections propagate
240
+ // through the full rejection chain to the RSC onError callback before
241
+ // we commit the response.
242
242
  //
243
- // The known rejection chain depth for a withSpan-wrapped page:
243
+ // Option 1 short-circuit: when Fizz's `stream.allReady` settled before
244
+ // the `deferSuspenseFor` hold ended (navContext._fizzAllReadyResolved),
245
+ // Flight has closed its stream, every `onError` already fired, and the
246
+ // signals object is fully populated. The retry loop would be a no-op.
247
+ // See design/spike-signal-promotion-protocol.md §"Option 1".
248
+ //
249
+ // Fallback path (hold timed out, no hold window, or shell-only return):
250
+ // the known rejection chain depth for a withSpan-wrapped page is 3+
251
+ // microtasks —
244
252
  // 1. PageComponent throws DenySignal/RedirectSignal
245
253
  // 2. withSpan catches and re-throws (microtask 1)
246
254
  // 3. TracedPage promise rejects (microtask 2)
247
255
  // 4. React Flight rejection handler → onError (microtask 3+)
248
- //
249
- // A single queueMicrotask only advances the queue by one step, which
250
- // is insufficient for chains deeper than 1. We retry up to
251
- // SIGNAL_PROMOTION_MAX_YIELDS (5) times, exiting early as soon as a
252
- // signal is detected. This tolerates future wrappers adding depth
253
- // without requiring code changes.
254
- //
255
- // When signals are already captured (onSignal already fired), skip the
256
- // yield entirely — react immediately. Uses queueMicrotask instead of
257
- // setTimeout(0) to avoid yielding to the full event loop (timers phase).
256
+ // so a bounded retry loop (max 5) is required to catch signals whose
257
+ // propagation is still in-flight when callSsr returns. The loop exits
258
+ // early as soon as a signal is detected. Uses queueMicrotask instead
259
+ // of setTimeout(0) to avoid yielding to the full event loop.
258
260
  // See design/05-streaming.md §"deferSuspenseFor and the Hold Window"
259
261
  if (!signals.redirectSignal && !signals.denySignal && !signals.renderError) {
260
262
  let yieldsConsumed = 0;
261
- for (let i = 0; i < SIGNAL_PROMOTION_MAX_YIELDS; i++) {
262
- await new Promise<void>((r) => queueMicrotask(r));
263
- yieldsConsumed++;
264
- if (signals.redirectSignal || signals.denySignal || signals.renderError) break;
263
+ if (!navContext._fizzAllReadyResolved) {
264
+ for (let i = 0; i < SIGNAL_PROMOTION_MAX_YIELDS; i++) {
265
+ await new Promise<void>((r) => queueMicrotask(r));
266
+ yieldsConsumed++;
267
+ if (signals.redirectSignal || signals.denySignal || signals.renderError) break;
268
+ }
265
269
  }
266
270
  __test_only_signalPromotionDepth.value = yieldsConsumed;
267
271
  }