@timber-js/app 0.1.51 → 0.1.53

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 (37) hide show
  1. package/dist/adapters/compress-module.d.ts.map +1 -1
  2. package/dist/adapters/nitro.d.ts.map +1 -1
  3. package/dist/adapters/nitro.js +22 -8
  4. package/dist/adapters/nitro.js.map +1 -1
  5. package/dist/client/index.js +248 -22
  6. package/dist/client/index.js.map +1 -1
  7. package/dist/client/router.d.ts +6 -0
  8. package/dist/client/router.d.ts.map +1 -1
  9. package/dist/client/rsc-fetch.d.ts +80 -0
  10. package/dist/client/rsc-fetch.d.ts.map +1 -0
  11. package/dist/client/segment-cache.d.ts +2 -0
  12. package/dist/client/segment-cache.d.ts.map +1 -1
  13. package/dist/client/segment-merger.d.ts +96 -0
  14. package/dist/client/segment-merger.d.ts.map +1 -0
  15. package/dist/client/stale-reload.d.ts +44 -0
  16. package/dist/client/stale-reload.d.ts.map +1 -0
  17. package/dist/index.js +15 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/server/compress.d.ts.map +1 -1
  20. package/dist/server/route-element-builder.d.ts +7 -0
  21. package/dist/server/route-element-builder.d.ts.map +1 -1
  22. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  23. package/dist/server/rsc-entry/rsc-payload.d.ts +1 -1
  24. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  25. package/package.json +1 -1
  26. package/src/adapters/compress-module.ts +18 -5
  27. package/src/adapters/nitro.ts +4 -3
  28. package/src/client/browser-entry.ts +68 -8
  29. package/src/client/router.ts +48 -188
  30. package/src/client/rsc-fetch.ts +234 -0
  31. package/src/client/segment-cache.ts +2 -0
  32. package/src/client/segment-merger.ts +297 -0
  33. package/src/client/stale-reload.ts +89 -0
  34. package/src/server/compress.ts +23 -4
  35. package/src/server/route-element-builder.ts +14 -0
  36. package/src/server/rsc-entry/index.ts +3 -2
  37. package/src/server/rsc-entry/rsc-payload.ts +8 -1
@@ -0,0 +1,80 @@
1
+ /**
2
+ * RSC Fetch — handles fetching and parsing RSC Flight payloads.
3
+ *
4
+ * Extracted from router.ts to keep both files under the 500-line limit.
5
+ * This module handles:
6
+ * - Cache-busting URL generation for RSC requests
7
+ * - Building RSC request headers (Accept, X-Timber-State-Tree)
8
+ * - Extracting metadata from RSC response headers
9
+ * - Fetching and decoding RSC payloads
10
+ *
11
+ * See design/19-client-navigation.md §"RSC Payload Handling"
12
+ */
13
+ import type { SegmentInfo } from './segment-cache';
14
+ import type { HeadElement } from './head';
15
+ import type { RouterDeps } from './router';
16
+ /** Result of fetching an RSC payload — includes head elements and segment metadata. */
17
+ export interface FetchResult {
18
+ payload: unknown;
19
+ headElements: HeadElement[] | null;
20
+ /** Segment metadata from X-Timber-Segments header for populating the segment cache. */
21
+ segmentInfo: SegmentInfo[] | null;
22
+ /** Route params from X-Timber-Params header for populating useParams(). */
23
+ params: Record<string, string | string[]> | null;
24
+ /** Segment paths that were skipped by the server (for client-side merging). */
25
+ skippedSegments: string[] | null;
26
+ }
27
+ export declare const RSC_CONTENT_TYPE = "text/x-component";
28
+ export declare function buildRscHeaders(stateTree: {
29
+ segments: string[];
30
+ } | undefined, currentUrl?: string): Record<string, string>;
31
+ /**
32
+ * Extract head elements from the X-Timber-Head response header.
33
+ * Returns null if the header is missing or malformed.
34
+ */
35
+ export declare function extractHeadElements(response: Response): HeadElement[] | null;
36
+ /**
37
+ * Extract segment metadata from the X-Timber-Segments response header.
38
+ * Returns null if the header is missing or malformed.
39
+ *
40
+ * Format: JSON array of {path, isAsync} objects describing the rendered
41
+ * segment chain from root to leaf. Used to populate the client-side
42
+ * segment cache for state tree diffing on subsequent navigations.
43
+ */
44
+ export declare function extractSegmentInfo(response: Response): SegmentInfo[] | null;
45
+ /**
46
+ * Extract skipped segment paths from the X-Timber-Skipped-Segments header.
47
+ * Returns null if the header is missing or malformed.
48
+ *
49
+ * When the server skips sync layouts the client already has cached,
50
+ * it sends this header listing the skipped segment paths (outermost first).
51
+ * The client uses this to merge the partial payload with cached segments.
52
+ */
53
+ export declare function extractSkippedSegments(response: Response): string[] | null;
54
+ /**
55
+ * Extract route params from the X-Timber-Params response header.
56
+ * Returns null if the header is missing or malformed.
57
+ *
58
+ * Used to populate useParams() after client-side navigation.
59
+ */
60
+ export declare function extractParams(response: Response): Record<string, string | string[]> | null;
61
+ /**
62
+ * Thrown when an RSC payload response contains X-Timber-Redirect header.
63
+ * Caught in navigate() to trigger a soft router navigation to the redirect target.
64
+ */
65
+ export declare class RedirectError extends Error {
66
+ readonly redirectUrl: string;
67
+ constructor(url: string);
68
+ }
69
+ /**
70
+ * Fetch an RSC payload from the server. If a decodeRsc function is provided,
71
+ * the response is decoded into a React element tree via createFromFetch.
72
+ * Otherwise, the raw response text is returned (test mode).
73
+ *
74
+ * Also extracts head elements from the X-Timber-Head response header
75
+ * so the client can update document.title and <meta> tags after navigation.
76
+ */
77
+ export declare function fetchRscPayload(url: string, deps: RouterDeps, stateTree?: {
78
+ segments: string[];
79
+ }, currentUrl?: string): Promise<FetchResult>;
80
+ //# sourceMappingURL=rsc-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsc-fetch.d.ts","sourceRoot":"","sources":["../../src/client/rsc-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnC,uFAAuF;IACvF,WAAW,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAClC,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IACjD,+EAA+E;IAC/E,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAClC;AAID,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AA2BnD,wBAAgB,eAAe,CAC7B,SAAS,EAAE;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,EAC7C,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,EAAE,GAAG,IAAI,CAQ5E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,EAAE,GAAG,IAAI,CAQ3E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,GAAG,IAAI,CAS1E;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAQ1F;AAID;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBACjB,GAAG,EAAE,MAAM;CAIxB;AAID;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,UAAU,EAChB,SAAS,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,EAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CAuDtB"}
@@ -7,6 +7,8 @@ export interface PrefetchResult {
7
7
  segmentInfo?: SegmentInfo[] | null;
8
8
  /** Route params from X-Timber-Params header for populating useParams(). */
9
9
  params?: Record<string, string | string[]> | null;
10
+ /** Segment paths skipped by the server (for client-side merging). */
11
+ skippedSegments?: string[] | null;
10
12
  }
11
13
  /**
12
14
  * A node in the client-side segment tree. Each node represents a mounted
@@ -1 +1 @@
1
- {"version":3,"file":"segment-cache.d.ts","sourceRoot":"","sources":["../../src/client/segment-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAI1C,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnC,uFAAuF;IACvF,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;CACnD;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAA0B;IAEtC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAO7C,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI;IAM7C,KAAK,IAAI,IAAI;IAIb;;;;;;;OAOG;IACH,kBAAkB,IAAI,SAAS;CAOhC;AAcD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,GAAG,SAAS,CA+BjF;AASD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAU;IACxC,OAAO,CAAC,OAAO,CAAoC;IAEnD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI;IAO9C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAU5C,0EAA0E;IAC1E,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;CAOjD"}
1
+ {"version":3,"file":"segment-cache.d.ts","sourceRoot":"","sources":["../../src/client/segment-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAI1C,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnC,uFAAuF;IACvF,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAClD,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAA0B;IAEtC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAO7C,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI;IAM7C,KAAK,IAAI,IAAI;IAIb;;;;;;;OAOG;IACH,kBAAkB,IAAI,SAAS;CAOhC;AAcD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,GAAG,SAAS,CA+BjF;AASD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAU;IACxC,OAAO,CAAC,OAAO,CAAoC;IAEnD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI;IAO9C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAU5C,0EAA0E;IAC1E,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;CAOjD"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Segment Merger — client-side tree merging for partial RSC payloads.
3
+ *
4
+ * When the server skips rendering sync layouts (because the client already
5
+ * has them cached), the RSC payload is missing outer segment wrappers.
6
+ * This module reconstructs the full element tree by splicing the partial
7
+ * payload into cached segment subtrees.
8
+ *
9
+ * The approach:
10
+ * 1. After each full RSC payload render, walk the decoded element tree
11
+ * and cache each segment's subtree (identified by SegmentProvider boundaries)
12
+ * 2. When a partial payload arrives, wrap it with cached segment elements
13
+ * using React.cloneElement to preserve component identity
14
+ *
15
+ * React.cloneElement preserves the element's `type` — React sees the same
16
+ * component at the same tree position and reconciles (preserving state)
17
+ * rather than remounting. This is how layout state survives navigations.
18
+ *
19
+ * Design docs: 19-client-navigation.md §"Navigation Reconciliation"
20
+ * Security: access.ts runs on the server regardless of skipping — this
21
+ * is a performance optimization only. See 13-security.md.
22
+ */
23
+ import { type ReactElement, type ReactNode } from 'react';
24
+ /**
25
+ * A cached segment entry. Stores the full subtree rooted at a SegmentProvider
26
+ * and the path through the tree to the next SegmentProvider (or leaf).
27
+ */
28
+ export interface CachedSegmentEntry {
29
+ /** The segment's URL path (e.g., "/", "/dashboard") */
30
+ segmentPath: string;
31
+ /** The SegmentProvider element for this segment */
32
+ element: ReactElement;
33
+ }
34
+ /**
35
+ * Cache of React element subtrees per segment path.
36
+ * Updated after each navigation with the full decoded RSC element tree.
37
+ */
38
+ export declare class SegmentElementCache {
39
+ private entries;
40
+ get(segmentPath: string): CachedSegmentEntry | undefined;
41
+ set(segmentPath: string, entry: CachedSegmentEntry): void;
42
+ has(segmentPath: string): boolean;
43
+ clear(): void;
44
+ get size(): number;
45
+ }
46
+ /**
47
+ * Check if a React element is a SegmentProvider by looking for the
48
+ * `segments` prop (an array of path segments). This is the only
49
+ * component that receives this prop shape.
50
+ */
51
+ export declare function isSegmentProvider(element: unknown): element is ReactElement;
52
+ /**
53
+ * Extract the segment path from a SegmentProvider element.
54
+ * The `segments` prop is an array like ["", "dashboard", "settings"].
55
+ * The path is reconstructed as "/" + segments.filter(Boolean).join("/").
56
+ */
57
+ export declare function getSegmentPath(element: ReactElement): string;
58
+ /**
59
+ * Walk a React element tree and extract all SegmentProvider boundaries.
60
+ * Returns an ordered list of segment entries from outermost to innermost.
61
+ *
62
+ * This only finds SegmentProviders along the main children path — it does
63
+ * not descend into parallel routes/slots (those are separate subtrees).
64
+ */
65
+ export declare function extractSegments(element: unknown): CachedSegmentEntry[];
66
+ /**
67
+ * Cache all segment subtrees from a fully-rendered RSC element tree.
68
+ * Call this after every full RSC payload render (navigate, refresh, hydration).
69
+ */
70
+ export declare function cacheSegmentElements(element: unknown, cache: SegmentElementCache): void;
71
+ /**
72
+ * Replace a nested SegmentProvider within a cached element tree with
73
+ * new content. Uses cloneElement along the path to produce a new tree
74
+ * with preserved component identity at every level except the replaced node.
75
+ *
76
+ * @param cachedElement The cached SegmentProvider element for this segment
77
+ * @param newInnerContent The new React element to splice in at the inner segment position
78
+ * @param innerSegmentPath The path of the inner segment to replace (optional — replaces first found)
79
+ * @returns New element tree with the inner segment replaced
80
+ */
81
+ export declare function replaceInnerSegment(cachedElement: ReactElement, newInnerContent: ReactNode, innerSegmentPath?: string): ReactElement;
82
+ /**
83
+ * Merge a partial RSC payload with cached segment elements.
84
+ *
85
+ * When the server skips segments, the partial payload starts from the
86
+ * first non-skipped segment. This function wraps it with cached elements
87
+ * for the skipped segments, producing a full tree that React can
88
+ * reconcile with the mounted tree (preserving layout state).
89
+ *
90
+ * @param partialPayload The RSC payload element (may be partial)
91
+ * @param skippedSegments Ordered list of segment paths that were skipped (outermost first)
92
+ * @param cache The segment element cache
93
+ * @returns The merged full element tree, or the partial payload if merging isn't possible
94
+ */
95
+ export declare function mergeSegmentTree(partialPayload: unknown, skippedSegments: string[], cache: SegmentElementCache): unknown;
96
+ //# sourceMappingURL=segment-merger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-merger.d.ts","sourceRoot":"","sources":["../../src/client/segment-merger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAgC,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIxF;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,OAAO,EAAE,YAAY,CAAC;CACvB;AAID;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAyC;IAExD,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIxD,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAIzD,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIjC,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAID;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,YAAY,CAI3E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAI5D;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,kBAAkB,EAAE,CAItE;AAsCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,mBAAmB,GACzB,IAAI,CAKN;AAqDD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,YAAY,EAC3B,eAAe,EAAE,SAAS,EAC1B,gBAAgB,CAAC,EAAE,MAAM,GACxB,YAAY,CA6Bd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,cAAc,EAAE,OAAO,EACvB,eAAe,EAAE,MAAM,EAAE,EACzB,KAAK,EAAE,mBAAmB,GACzB,OAAO,CA0BT"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Stale Client Reference Reload
3
+ *
4
+ * When a new deployment ships updated bundles, clients running stale
5
+ * JavaScript may encounter "Could not find the module" errors during
6
+ * RSC Flight stream decoding. This happens because the RSC payload
7
+ * references module IDs from the new bundle that don't exist in the
8
+ * old client bundle.
9
+ *
10
+ * This module detects these specific errors and triggers a full page
11
+ * reload so the browser fetches the new bundle. A sessionStorage flag
12
+ * guards against infinite reload loops.
13
+ *
14
+ * See: LOCAL-332
15
+ */
16
+ /**
17
+ * Check if an error is a stale client reference error from React's
18
+ * Flight client. These errors have the message pattern:
19
+ * "Could not find the module \"<id>\""
20
+ *
21
+ * This is thrown by react-server-dom-webpack's client when the RSC
22
+ * payload references a module ID that doesn't exist in the client's
23
+ * module map — typically because the server has been redeployed with
24
+ * new bundle hashes while the client is still running old JavaScript.
25
+ */
26
+ export declare function isStaleClientReference(error: unknown): boolean;
27
+ /**
28
+ * Trigger a full page reload to pick up new bundles.
29
+ *
30
+ * Sets a sessionStorage flag before reloading. If the flag is already
31
+ * set (meaning we already reloaded once for this reason), we don't
32
+ * reload again — this prevents infinite reload loops if the error
33
+ * persists after reload (e.g., a genuine bug rather than a stale bundle).
34
+ *
35
+ * @returns true if a reload was triggered, false if suppressed (loop guard)
36
+ */
37
+ export declare function triggerStaleReload(): boolean;
38
+ /**
39
+ * Clear the stale reload flag. Called on successful bootstrap to reset
40
+ * the loop guard — if the page loaded successfully, the next stale
41
+ * reference error should trigger a fresh reload attempt.
42
+ */
43
+ export declare function clearStaleReloadFlag(): void;
44
+ //# sourceMappingURL=stale-reload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-reload.d.ts","sourceRoot":"","sources":["../../src/client/stale-reload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CA+B5C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAM3C"}
package/dist/index.js CHANGED
@@ -11560,7 +11560,21 @@ var NO_COMPRESS_STATUSES = new Set([204, 304]);
11560
11560
  */
11561
11561
  function negotiateEncoding(acceptEncoding) {
11562
11562
  if (!acceptEncoding) return null;
11563
- if (acceptEncoding.split(",").map((s) => s.split(";")[0].trim().toLowerCase()).includes("gzip")) return "gzip";
11563
+ const parts = acceptEncoding.split(",");
11564
+ for (const part of parts) {
11565
+ const [token, ...params] = part.split(";");
11566
+ if (token.trim().toLowerCase() !== "gzip") continue;
11567
+ let qValue = 1;
11568
+ for (const param of params) {
11569
+ const trimmed = param.trim().toLowerCase();
11570
+ if (trimmed.startsWith("q=")) {
11571
+ qValue = parseFloat(trimmed.slice(2));
11572
+ if (Number.isNaN(qValue)) qValue = 1;
11573
+ break;
11574
+ }
11575
+ }
11576
+ if (qValue > 0) return "gzip";
11577
+ }
11564
11578
  return null;
11565
11579
  }
11566
11580
  /**