@timber-js/app 0.2.0-alpha.124 → 0.2.0-alpha.125

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 (58) hide show
  1. package/dist/_chunks/{build-output-helper-DYOWsPjZ.js → build-output-helper-K7oUCRqU.js} +1 -1
  2. package/dist/_chunks/{build-output-helper-DYOWsPjZ.js.map → build-output-helper-K7oUCRqU.js.map} +1 -1
  3. package/dist/_chunks/{navigation-root-DaERBEGV.js → navigation-root-CeNTCxzS.js} +5 -5
  4. package/dist/_chunks/navigation-root-CeNTCxzS.js.map +1 -0
  5. package/dist/_chunks/{stale-reload-BN2kcE06.js → stale-reload-CCqb62Z3.js} +22 -2
  6. package/dist/_chunks/{stale-reload-BN2kcE06.js.map → stale-reload-CCqb62Z3.js.map} +1 -1
  7. package/dist/adapters/cloudflare.js +1 -1
  8. package/dist/adapters/nitro.js +1 -1
  9. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  10. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  11. package/dist/client/browser-entry/router-init.d.ts +4 -1
  12. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  13. package/dist/client/index.js +3 -3
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/client/internal.js +127 -269
  16. package/dist/client/internal.js.map +1 -1
  17. package/dist/client/link-pending-store.d.ts +19 -17
  18. package/dist/client/link-pending-store.d.ts.map +1 -1
  19. package/dist/client/link.d.ts.map +1 -1
  20. package/dist/client/navigation-root.d.ts +4 -1
  21. package/dist/client/navigation-root.d.ts.map +1 -1
  22. package/dist/client/router.d.ts +10 -7
  23. package/dist/client/router.d.ts.map +1 -1
  24. package/dist/client/rsc-fetch.d.ts.map +1 -1
  25. package/dist/client/segment-cache.d.ts +2 -0
  26. package/dist/client/segment-cache.d.ts.map +1 -1
  27. package/dist/client/segment-outlet.d.ts +23 -37
  28. package/dist/client/segment-outlet.d.ts.map +1 -1
  29. package/dist/client/segment-update-context.d.ts +21 -0
  30. package/dist/client/segment-update-context.d.ts.map +1 -0
  31. package/dist/index.js +1 -1
  32. package/dist/server/internal.js +57 -1
  33. package/dist/server/internal.js.map +1 -1
  34. package/dist/server/route-element-builder.d.ts.map +1 -1
  35. package/dist/server/rsc-entry/helpers.d.ts +1 -0
  36. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  37. package/dist/server/state-tree-diff.d.ts +12 -5
  38. package/dist/server/state-tree-diff.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/client/browser-entry/hydrate.ts +7 -1
  41. package/src/client/browser-entry/index.ts +3 -1
  42. package/src/client/browser-entry/post-hydration.ts +0 -13
  43. package/src/client/browser-entry/router-init.ts +50 -24
  44. package/src/client/link-pending-store.ts +22 -17
  45. package/src/client/link.tsx +12 -6
  46. package/src/client/navigation-root.tsx +40 -18
  47. package/src/client/router.ts +109 -97
  48. package/src/client/rsc-fetch.ts +85 -6
  49. package/src/client/segment-cache.ts +5 -2
  50. package/src/client/segment-outlet.tsx +40 -49
  51. package/src/client/segment-update-context.ts +42 -0
  52. package/src/server/route-element-builder.ts +69 -2
  53. package/src/server/rsc-entry/helpers.ts +18 -14
  54. package/src/server/state-tree-diff.ts +26 -22
  55. package/dist/_chunks/navigation-root-DaERBEGV.js.map +0 -1
  56. package/dist/client/segment-merger.d.ts +0 -119
  57. package/dist/client/segment-merger.d.ts.map +0 -1
  58. package/src/client/segment-merger.ts +0 -339
@@ -1,27 +1,24 @@
1
1
  /**
2
2
  * SegmentOutlet — client component boundary at each layout segment.
3
3
  *
4
- * Replaces the post-hoc tree walking in segment-merger.ts with an explicit
5
- * client component at each segment boundary. Each outlet:
4
+ * Each layout in the segment tree is wrapped with a SegmentOutlet that:
5
+ * 1. Knows its segment path (prop from the server)
6
+ * 2. Reads from SegmentUpdateContext for partial navigation updates
7
+ * 3. Caches its rendered content in a ref across navigations
6
8
  *
7
- * 1. Knows its own segment path (prop from the server)
8
- * 2. Caches its children in a ref across navigations
9
- * 3. When `keepCurrent` is true (partial navigation, this segment skipped),
10
- * returns the previously cached children — layout state is preserved
11
- * 4. When `keepCurrent` is false (full navigation or this segment changed),
12
- * stores and renders the new children
9
+ * On full navigation: receives new children via props, caches and renders them.
10
+ * On partial navigation (this segment skipped): the context map has no entry
11
+ * for this path, so the outlet returns cached content — preserving layout state.
12
+ * On partial navigation (this segment updated): the context map has content
13
+ * for this path, so the outlet renders the update.
13
14
  *
14
- * This eliminates the need for client-side element tree walking, which
15
- * breaks on real RSC trees due to opaque client component lazy refs,
16
- * Suspense thenables, and AccessGate wrappers.
15
+ * Uses React context instead of useSyncExternalStore to stay compatible
16
+ * with concurrent rendering (transitions). All outlets re-render when the
17
+ * context value changes, but each bails out quickly if its segment has
18
+ * no update — same approach as Next.js LayoutRouter.
17
19
  *
18
- * Architecture is similar to Next.js's `<LayoutRouter>` client component —
19
- * each layout boundary is an explicit client component that manages its
20
- * own subtree. See design/19-client-navigation.md.
21
- *
22
- * Security: This is a performance optimization only. The server always
23
- * runs all access.ts files regardless of segment skipping. A fabricated
24
- * keepCurrent prop can only cause stale layouts — never auth bypass.
20
+ * Security: performance optimization only. The server always runs all
21
+ * access.ts files regardless of segment skipping.
25
22
  * See design/13-security.md §"State tree manipulation".
26
23
  */
27
24
  import { type ReactNode } from 'react';
@@ -33,31 +30,20 @@ export interface SegmentOutletProps {
33
30
  * the same urlPath. Must match the segmentId used in state-tree-diff.ts.
34
31
  */
35
32
  segmentPath: string;
36
- /**
37
- * When true, the outlet returns its previously cached children instead
38
- * of rendering the new children prop. Set by the server when this
39
- * segment was skipped (the client already has the layout mounted).
40
- *
41
- * On the first render (SSR/hydration), this is always false — there's
42
- * no cached content yet. On subsequent partial navigations, the server
43
- * sets this to true for segments it skipped rendering.
44
- */
45
- keepCurrent?: boolean;
46
33
  /** The segment's React subtree (layout + inner content). */
47
34
  children: ReactNode;
48
35
  }
49
36
  /**
50
37
  * Client component boundary at each layout segment in the element tree.
51
38
  *
52
- * On full navigation: receives new children, stores them, renders them.
53
- * On partial navigation (keepCurrent=true): ignores children prop,
54
- * returns previously stored content — React reconciles the same elements,
55
- * preserving all client component state in the layout subtree.
39
+ * On full navigation (context map is empty): renders children, caches in ref.
40
+ * On partial navigation: checks the context map for an update at segmentPath.
41
+ * - Update found → renders update, caches in ref.
42
+ * - No update → returns cached content (layout state preserved).
56
43
  *
57
- * React preserves the ref across `reactRoot.render()` calls because:
58
- * - SegmentOutlet has a stable type (client component module reference)
59
- * - It appears at the same tree position on every navigation
60
- * - React reconciles same-type, same-position → instance preserved
44
+ * React preserves component instances across reactRoot.render() calls
45
+ * when the same type appears at the same tree position. The ref persists
46
+ * across navigations because SegmentOutlet is reconciled, not remounted.
61
47
  */
62
- export declare function SegmentOutlet({ segmentPath: _segmentPath, keepCurrent, children, }: SegmentOutletProps): ReactNode;
48
+ export declare function SegmentOutlet({ segmentPath, children }: SegmentOutletProps): ReactNode;
63
49
  //# sourceMappingURL=segment-outlet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"segment-outlet.d.ts","sourceRoot":"","sources":["../../src/client/segment-outlet.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,4DAA4D;IAC5D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EAAE,YAAY,EACzB,WAAmB,EACnB,QAAQ,GACT,EAAE,kBAAkB,aAapB"}
1
+ {"version":3,"file":"segment-outlet.d.ts","sourceRoot":"","sources":["../../src/client/segment-outlet.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG3D,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB,4DAA4D;IAC5D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,kBAAkB,aAsB1E"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SegmentUpdateContext — React context for partial navigation updates.
3
+ *
4
+ * During partial navigation (server skips unchanged sync layouts), the
5
+ * router builds a Map of segment path → ReactNode updates and passes it
6
+ * as the context value. Mounted SegmentOutlet components read from this
7
+ * context to decide whether to render the update or their cached content.
8
+ *
9
+ * SINGLETON GUARANTEE: Uses globalThis + Symbol.for — same pattern as
10
+ * NavigationContext. The RSC client bundler can duplicate this module
11
+ * across chunks (browser-entry graph + client-reference graph). With
12
+ * ESM output, each chunk gets its own module scope — a bare createContext
13
+ * at module level would create separate instances per chunk. globalThis
14
+ * guarantees a single instance regardless of duplication.
15
+ *
16
+ * See design/19-client-navigation.md §"Singleton Guarantee via globalThis"
17
+ */
18
+ import React from 'react';
19
+ export declare const EMPTY_SEGMENT_UPDATES: Map<string, React.ReactNode>;
20
+ export declare const SegmentUpdateContext: React.Context<Map<string, React.ReactNode>>;
21
+ //# sourceMappingURL=segment-update-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment-update-context.d.ts","sourceRoot":"","sources":["../../src/client/segment-update-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAyB,MAAM,OAAO,CAAC;AAE9C,eAAO,MAAM,qBAAqB,8BAA+B,CAAC;AAmBlE,eAAO,MAAM,oBAAoB,6CAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -7240,6 +7240,6 @@ function timber(config) {
7240
7240
  ];
7241
7241
  }
7242
7242
  //#endregion
7243
- export { timber as default, timber, defineConfig };
7243
+ export { timber as default, timber, defineConfig, __exportAll as t };
7244
7244
 
7245
7245
  //# sourceMappingURL=index.js.map
@@ -9,7 +9,7 @@ import { n as toBracketKey } from "../_chunks/resolve-schema-DU2nbVOi.js";
9
9
  import "../_chunks/segment-context-Bg0pFbLT.js";
10
10
  import { readFile } from "node:fs/promises";
11
11
  import "node:crypto";
12
- import { createElement } from "react";
12
+ import React, { createElement } from "react";
13
13
  //#region src/server/server-timing.ts
14
14
  /**
15
15
  * Server-Timing header — dev-mode timing breakdowns for Chrome DevTools.
@@ -1401,6 +1401,62 @@ async function coerceSegmentParams(match) {
1401
1401
  }
1402
1402
  }
1403
1403
  //#endregion
1404
+ //#region src/client/segment-update-context.ts
1405
+ /**
1406
+ * SegmentUpdateContext — React context for partial navigation updates.
1407
+ *
1408
+ * During partial navigation (server skips unchanged sync layouts), the
1409
+ * router builds a Map of segment path → ReactNode updates and passes it
1410
+ * as the context value. Mounted SegmentOutlet components read from this
1411
+ * context to decide whether to render the update or their cached content.
1412
+ *
1413
+ * SINGLETON GUARANTEE: Uses globalThis + Symbol.for — same pattern as
1414
+ * NavigationContext. The RSC client bundler can duplicate this module
1415
+ * across chunks (browser-entry graph + client-reference graph). With
1416
+ * ESM output, each chunk gets its own module scope — a bare createContext
1417
+ * at module level would create separate instances per chunk. globalThis
1418
+ * guarantees a single instance regardless of duplication.
1419
+ *
1420
+ * See design/19-client-navigation.md §"Singleton Guarantee via globalThis"
1421
+ */
1422
+ var EMPTY_SEGMENT_UPDATES = /* @__PURE__ */ new Map();
1423
+ var CTX_KEY = Symbol.for("__timber_segment_update_ctx");
1424
+ function getOrCreateContext() {
1425
+ const existing = globalThis[CTX_KEY];
1426
+ if (existing !== void 0) return existing;
1427
+ if (typeof React.createContext === "function") {
1428
+ const ctx = React.createContext(EMPTY_SEGMENT_UPDATES);
1429
+ globalThis[CTX_KEY] = ctx;
1430
+ return ctx;
1431
+ }
1432
+ }
1433
+ getOrCreateContext();
1434
+ //#endregion
1435
+ //#region src/client/segment-outlet.tsx
1436
+ /**
1437
+ * SegmentOutlet — client component boundary at each layout segment.
1438
+ *
1439
+ * Each layout in the segment tree is wrapped with a SegmentOutlet that:
1440
+ * 1. Knows its segment path (prop from the server)
1441
+ * 2. Reads from SegmentUpdateContext for partial navigation updates
1442
+ * 3. Caches its rendered content in a ref across navigations
1443
+ *
1444
+ * On full navigation: receives new children via props, caches and renders them.
1445
+ * On partial navigation (this segment skipped): the context map has no entry
1446
+ * for this path, so the outlet returns cached content — preserving layout state.
1447
+ * On partial navigation (this segment updated): the context map has content
1448
+ * for this path, so the outlet renders the update.
1449
+ *
1450
+ * Uses React context instead of useSyncExternalStore to stay compatible
1451
+ * with concurrent rendering (transitions). All outlets re-render when the
1452
+ * context value changes, but each bails out quickly if its segment has
1453
+ * no update — same approach as Next.js LayoutRouter.
1454
+ *
1455
+ * Security: performance optimization only. The server always runs all
1456
+ * access.ts files regardless of segment skipping.
1457
+ * See design/13-security.md §"State tree manipulation".
1458
+ */
1459
+ //#endregion
1404
1460
  //#region src/server/route-element-builder.ts
1405
1461
  /**
1406
1462
  * Thrown when a defineSegmentParams codec's parse() fails.