@timber-js/app 0.2.0-alpha.167 → 0.2.0-alpha.168

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 (152) hide show
  1. package/dist/_chunks/{actions-TSxpXLHJ.js → actions-O_LsyCE4.js} +3 -3
  2. package/dist/_chunks/{actions-TSxpXLHJ.js.map → actions-O_LsyCE4.js.map} +1 -1
  3. package/dist/_chunks/{cache-api-DzpQQOEx.js → cache-api-B-lhk9p4.js} +56 -11
  4. package/dist/_chunks/cache-api-B-lhk9p4.js.map +1 -0
  5. package/dist/_chunks/{cli-schema-sync-wX-i90Og.js → cli-schema-sync-B73L6pMq.js} +2 -2
  6. package/dist/_chunks/{cli-schema-sync-wX-i90Og.js.map → cli-schema-sync-B73L6pMq.js.map} +1 -1
  7. package/dist/_chunks/json-lossy-check-ClNvBM_3.js +63 -0
  8. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +1 -0
  9. package/dist/_chunks/{logger-t3uxAmbX.js → logger-AWfuX-KJ.js} +2 -19
  10. package/dist/_chunks/logger-AWfuX-KJ.js.map +1 -0
  11. package/dist/_chunks/{walkers-Cfwvl-UC.js → walkers-CoOC8Hga.js} +2 -2
  12. package/dist/_chunks/{walkers-Cfwvl-UC.js.map → walkers-CoOC8Hga.js.map} +1 -1
  13. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -1
  14. package/dist/adapters/cloudflare-kv-cache.js +9 -2
  15. package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
  16. package/dist/cache/cache-api.d.ts.map +1 -1
  17. package/dist/cache/index.d.ts +1 -1
  18. package/dist/cache/index.d.ts.map +1 -1
  19. package/dist/cache/index.js +1 -1
  20. package/dist/cache/json-lossy-check.d.ts +11 -0
  21. package/dist/cache/json-lossy-check.d.ts.map +1 -0
  22. package/dist/cache/redis-handler.d.ts +42 -0
  23. package/dist/cache/redis-handler.d.ts.map +1 -1
  24. package/dist/cache/singleflight.d.ts.map +1 -1
  25. package/dist/cache/stores/cloudflare-kv.d.ts +1 -1
  26. package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -1
  27. package/dist/cache/stores/memory.d.ts +1 -1
  28. package/dist/cache/stores/memory.d.ts.map +1 -1
  29. package/dist/cache/stores/redis.d.ts +1 -1
  30. package/dist/cache/stores/redis.d.ts.map +1 -1
  31. package/dist/cache/stores/vercel.d.ts +1 -1
  32. package/dist/cache/stores/vercel.d.ts.map +1 -1
  33. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  34. package/dist/cache/timber-cache.d.ts.map +1 -1
  35. package/dist/cdn/cloudflare-purge.d.ts.map +1 -1
  36. package/dist/cdn/fastly-purge.d.ts.map +1 -1
  37. package/dist/cdn/workers-cache-purge.d.ts.map +1 -1
  38. package/dist/cli.d.ts +1 -1
  39. package/dist/cli.d.ts.map +1 -1
  40. package/dist/cli.js +3 -2
  41. package/dist/cli.js.map +1 -1
  42. package/dist/client/browser-entry/router-init.d.ts +1 -0
  43. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  44. package/dist/client/child-segment-context.d.ts +2 -2
  45. package/dist/client/child-segment-context.d.ts.map +1 -1
  46. package/dist/client/error-boundary.d.ts.map +1 -1
  47. package/dist/client/history.d.ts.map +1 -1
  48. package/dist/client/internal.js +4 -3
  49. package/dist/client/internal.js.map +1 -1
  50. package/dist/client/router.d.ts +6 -0
  51. package/dist/client/router.d.ts.map +1 -1
  52. package/dist/client/rsc-fetch.d.ts.map +1 -1
  53. package/dist/client/segment-cache.d.ts.map +1 -1
  54. package/dist/client/segment-update-context.d.ts +3 -3
  55. package/dist/client/segment-update-context.d.ts.map +1 -1
  56. package/dist/client/slot-outlet.d.ts +1 -1
  57. package/dist/codec.d.ts.map +1 -1
  58. package/dist/codec.js +2 -1
  59. package/dist/codec.js.map +1 -1
  60. package/dist/config-types.d.ts +16 -37
  61. package/dist/config-types.d.ts.map +1 -1
  62. package/dist/config-validation.d.ts.map +1 -1
  63. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  64. package/dist/fonts/pipeline.d.ts +19 -0
  65. package/dist/fonts/pipeline.d.ts.map +1 -1
  66. package/dist/fonts/transform.d.ts.map +1 -1
  67. package/dist/fonts/virtual-modules.d.ts.map +1 -1
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +344 -84
  70. package/dist/index.js.map +1 -1
  71. package/dist/plugin-context.d.ts.map +1 -1
  72. package/dist/plugins/adapter-build.d.ts +1 -0
  73. package/dist/plugins/adapter-build.d.ts.map +1 -1
  74. package/dist/plugins/entries.d.ts +27 -3
  75. package/dist/plugins/entries.d.ts.map +1 -1
  76. package/dist/plugins/fonts.d.ts.map +1 -1
  77. package/dist/plugins/server-bundle.d.ts.map +1 -1
  78. package/dist/routing/index.js +2 -2
  79. package/dist/server/action-client.d.ts +1 -1
  80. package/dist/server/action-client.d.ts.map +1 -1
  81. package/dist/server/body-limits.d.ts.map +1 -1
  82. package/dist/server/form-data.d.ts.map +1 -1
  83. package/dist/server/html-injector-core.d.ts.map +1 -1
  84. package/dist/server/index.js +20 -3
  85. package/dist/server/index.js.map +1 -1
  86. package/dist/server/internal.js +24 -40
  87. package/dist/server/internal.js.map +1 -1
  88. package/dist/server/middleware-runner.d.ts +0 -17
  89. package/dist/server/middleware-runner.d.ts.map +1 -1
  90. package/dist/server/param-coercion.d.ts.map +1 -1
  91. package/dist/server/pipeline-phases.d.ts +6 -3
  92. package/dist/server/pipeline-phases.d.ts.map +1 -1
  93. package/dist/server/pipeline.d.ts +18 -0
  94. package/dist/server/pipeline.d.ts.map +1 -1
  95. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  96. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  97. package/dist/server/primitives.d.ts.map +1 -1
  98. package/dist/server/render-timeout.d.ts.map +1 -1
  99. package/dist/server/route-element-builder.d.ts.map +1 -1
  100. package/dist/server/rsc-entry/action-middleware-runner.d.ts +14 -14
  101. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  102. package/dist/server/rsc-entry/render-route.d.ts +1 -0
  103. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  104. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  105. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +44 -74
  106. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  107. package/dist/server/safe-load.d.ts.map +1 -1
  108. package/dist/server/ssr-entry.d.ts.map +1 -1
  109. package/dist/shared/redirect-type.d.ts +2 -2
  110. package/dist/shared/redirect-type.d.ts.map +1 -1
  111. package/dist/shims/font-google.d.ts.map +1 -1
  112. package/dist/shims/image.d.ts +120 -120
  113. package/dist/shims/image.d.ts.map +1 -1
  114. package/docs/api/32-api-cache.mdx +112 -0
  115. package/docs/api/34-api-config.mdx +0 -26
  116. package/docs/learn/09-caching.mdx +121 -5
  117. package/docs/learn/12-client-navigation.mdx +9 -1
  118. package/docs/learn/13-configuration.mdx +6 -8
  119. package/docs/learn/14-deploying.mdx +18 -18
  120. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  121. package/package.json +6 -6
  122. package/src/adapters/cloudflare-kv-cache.ts +27 -6
  123. package/src/cache/index.ts +1 -1
  124. package/src/cache/json-lossy-check.ts +75 -0
  125. package/src/cache/redis-handler.ts +65 -14
  126. package/src/cache/timber-cache.ts +10 -2
  127. package/src/client/browser-entry/index.ts +2 -0
  128. package/src/client/browser-entry/router-init.ts +2 -0
  129. package/src/client/router.ts +13 -3
  130. package/src/codec.ts +3 -1
  131. package/src/config-types.ts +16 -37
  132. package/src/config-validation.ts +7 -5
  133. package/src/fonts/pipeline.ts +32 -0
  134. package/src/fonts/transform.ts +30 -24
  135. package/src/plugins/adapter-build.ts +131 -13
  136. package/src/plugins/entries.ts +40 -43
  137. package/src/plugins/fonts.ts +30 -0
  138. package/src/plugins/server-bundle.ts +7 -15
  139. package/src/server/action-client.ts +1 -1
  140. package/src/server/middleware-runner.ts +0 -44
  141. package/src/server/param-coercion.ts +1 -0
  142. package/src/server/pipeline-phases.ts +11 -13
  143. package/src/server/pipeline.ts +51 -3
  144. package/src/server/route-element-builder.ts +10 -1
  145. package/src/server/rsc-entry/action-middleware-runner.ts +14 -14
  146. package/src/server/rsc-entry/index.ts +30 -40
  147. package/src/server/rsc-entry/render-route.ts +3 -1
  148. package/src/server/rsc-entry/wrap-action-dispatch.ts +109 -372
  149. package/dist/_chunks/cache-api-DzpQQOEx.js.map +0 -1
  150. package/dist/_chunks/logger-t3uxAmbX.js.map +0 -1
  151. package/dist/_chunks/tree-match-D2l830j2.js +0 -102
  152. package/dist/_chunks/tree-match-D2l830j2.js.map +0 -1
@@ -31,126 +31,6 @@ export type ImageProps = ImgHTMLAttributes<HTMLImageElement> & {
31
31
  * props (priority, quality, fill, placeholder, blurDataURL).
32
32
  */
33
33
  export declare function Image({ priority: _priority, quality: _quality, fill: _fill, placeholder: _placeholder, blurDataURL: _blurDataURL, ...rest }: ImageProps): import("react").DetailedReactHTMLElement<{
34
- alt?: string | undefined;
35
- crossOrigin?: "" | "anonymous" | "use-credentials" | undefined;
36
- decoding?: "async" | "auto" | "sync" | undefined;
37
- fetchPriority?: "high" | "low" | "auto" | undefined;
38
- height?: number | string | undefined;
39
- loading?: "eager" | "lazy" | undefined;
40
- referrerPolicy?: import("react").HTMLAttributeReferrerPolicy | undefined;
41
- sizes?: string | undefined;
42
- src?: string | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_IMG_SRC_TYPES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_IMG_SRC_TYPES] | undefined;
43
- srcSet?: string | undefined;
44
- useMap?: string | undefined;
45
- width?: number | string | undefined;
46
- defaultChecked?: boolean | undefined;
47
- defaultValue?: string | number | readonly string[] | undefined;
48
- suppressContentEditableWarning?: boolean | undefined;
49
- suppressHydrationWarning?: boolean | undefined;
50
- accessKey?: string | undefined;
51
- autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {});
52
- autoFocus?: boolean | undefined;
53
- className?: string | undefined;
54
- contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
55
- contextMenu?: string | undefined;
56
- dir?: string | undefined;
57
- draggable?: (boolean | "true" | "false") | undefined;
58
- enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined;
59
- hidden?: boolean | undefined;
60
- id?: string | undefined;
61
- lang?: string | undefined;
62
- nonce?: string | undefined;
63
- slot?: string | undefined;
64
- spellCheck?: (boolean | "true" | "false") | undefined;
65
- style?: import("react").CSSProperties | undefined;
66
- tabIndex?: number | undefined;
67
- title?: string | undefined;
68
- translate?: "yes" | "no" | undefined;
69
- radioGroup?: string | undefined;
70
- role?: import("react").AriaRole | undefined;
71
- about?: string | undefined;
72
- content?: string | undefined;
73
- datatype?: string | undefined;
74
- inlist?: any;
75
- prefix?: string | undefined;
76
- property?: string | undefined;
77
- rel?: string | undefined;
78
- resource?: string | undefined;
79
- rev?: string | undefined;
80
- typeof?: string | undefined;
81
- vocab?: string | undefined;
82
- autoCorrect?: string | undefined;
83
- autoSave?: string | undefined;
84
- color?: string | undefined;
85
- itemProp?: string | undefined;
86
- itemScope?: boolean | undefined;
87
- itemType?: string | undefined;
88
- itemID?: string | undefined;
89
- itemRef?: string | undefined;
90
- results?: number | undefined;
91
- security?: string | undefined;
92
- unselectable?: "on" | "off" | undefined;
93
- popover?: "" | "auto" | "manual" | "hint" | undefined;
94
- popoverTargetAction?: "toggle" | "show" | "hide" | undefined;
95
- popoverTarget?: string | undefined;
96
- inert?: boolean | undefined;
97
- inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined;
98
- is?: string | undefined;
99
- exportparts?: string | undefined;
100
- part?: string | undefined;
101
- "aria-activedescendant"?: string | undefined;
102
- "aria-atomic"?: (boolean | "true" | "false") | undefined;
103
- "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined;
104
- "aria-braillelabel"?: string | undefined;
105
- "aria-brailleroledescription"?: string | undefined;
106
- "aria-busy"?: (boolean | "true" | "false") | undefined;
107
- "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined;
108
- "aria-colcount"?: number | undefined;
109
- "aria-colindex"?: number | undefined;
110
- "aria-colindextext"?: string | undefined;
111
- "aria-colspan"?: number | undefined;
112
- "aria-controls"?: string | undefined;
113
- "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined;
114
- "aria-describedby"?: string | undefined;
115
- "aria-description"?: string | undefined;
116
- "aria-details"?: string | undefined;
117
- "aria-disabled"?: (boolean | "true" | "false") | undefined;
118
- "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined;
119
- "aria-errormessage"?: string | undefined;
120
- "aria-expanded"?: (boolean | "true" | "false") | undefined;
121
- "aria-flowto"?: string | undefined;
122
- "aria-grabbed"?: (boolean | "true" | "false") | undefined;
123
- "aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined;
124
- "aria-hidden"?: (boolean | "true" | "false") | undefined;
125
- "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined;
126
- "aria-keyshortcuts"?: string | undefined;
127
- "aria-label"?: string | undefined;
128
- "aria-labelledby"?: string | undefined;
129
- "aria-level"?: number | undefined;
130
- "aria-live"?: "off" | "assertive" | "polite" | undefined;
131
- "aria-modal"?: (boolean | "true" | "false") | undefined;
132
- "aria-multiline"?: (boolean | "true" | "false") | undefined;
133
- "aria-multiselectable"?: (boolean | "true" | "false") | undefined;
134
- "aria-orientation"?: "horizontal" | "vertical" | undefined;
135
- "aria-owns"?: string | undefined;
136
- "aria-placeholder"?: string | undefined;
137
- "aria-posinset"?: number | undefined;
138
- "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined;
139
- "aria-readonly"?: (boolean | "true" | "false") | undefined;
140
- "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined;
141
- "aria-required"?: (boolean | "true" | "false") | undefined;
142
- "aria-roledescription"?: string | undefined;
143
- "aria-rowcount"?: number | undefined;
144
- "aria-rowindex"?: number | undefined;
145
- "aria-rowindextext"?: string | undefined;
146
- "aria-rowspan"?: number | undefined;
147
- "aria-selected"?: (boolean | "true" | "false") | undefined;
148
- "aria-setsize"?: number | undefined;
149
- "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined;
150
- "aria-valuemax"?: number | undefined;
151
- "aria-valuemin"?: number | undefined;
152
- "aria-valuenow"?: number | undefined;
153
- "aria-valuetext"?: string | undefined;
154
34
  children?: import("react").ReactNode | undefined;
155
35
  dangerouslySetInnerHTML?: {
156
36
  __html: string | TrustedHTML;
@@ -323,6 +203,126 @@ export declare function Image({ priority: _priority, quality: _quality, fill: _f
323
203
  onTransitionRunCapture?: import("react").TransitionEventHandler<HTMLImageElement> | undefined;
324
204
  onTransitionStart?: import("react").TransitionEventHandler<HTMLImageElement> | undefined;
325
205
  onTransitionStartCapture?: import("react").TransitionEventHandler<HTMLImageElement> | undefined;
206
+ "aria-activedescendant"?: string | undefined;
207
+ "aria-atomic"?: ("false" | "true" | boolean) | undefined;
208
+ "aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined;
209
+ "aria-braillelabel"?: string | undefined;
210
+ "aria-brailleroledescription"?: string | undefined;
211
+ "aria-busy"?: ("false" | "true" | boolean) | undefined;
212
+ "aria-checked"?: boolean | "false" | "mixed" | "true" | undefined;
213
+ "aria-colcount"?: number | undefined;
214
+ "aria-colindex"?: number | undefined;
215
+ "aria-colindextext"?: string | undefined;
216
+ "aria-colspan"?: number | undefined;
217
+ "aria-controls"?: string | undefined;
218
+ "aria-current"?: boolean | "false" | "true" | "page" | "step" | "location" | "date" | "time" | undefined;
219
+ "aria-describedby"?: string | undefined;
220
+ "aria-description"?: string | undefined;
221
+ "aria-details"?: string | undefined;
222
+ "aria-disabled"?: ("false" | "true" | boolean) | undefined;
223
+ "aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined;
224
+ "aria-errormessage"?: string | undefined;
225
+ "aria-expanded"?: ("false" | "true" | boolean) | undefined;
226
+ "aria-flowto"?: string | undefined;
227
+ "aria-grabbed"?: ("false" | "true" | boolean) | undefined;
228
+ "aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined;
229
+ "aria-hidden"?: ("false" | "true" | boolean) | undefined;
230
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined;
231
+ "aria-keyshortcuts"?: string | undefined;
232
+ "aria-label"?: string | undefined;
233
+ "aria-labelledby"?: string | undefined;
234
+ "aria-level"?: number | undefined;
235
+ "aria-live"?: "off" | "assertive" | "polite" | undefined;
236
+ "aria-modal"?: ("false" | "true" | boolean) | undefined;
237
+ "aria-multiline"?: ("false" | "true" | boolean) | undefined;
238
+ "aria-multiselectable"?: ("false" | "true" | boolean) | undefined;
239
+ "aria-orientation"?: "horizontal" | "vertical" | undefined;
240
+ "aria-owns"?: string | undefined;
241
+ "aria-placeholder"?: string | undefined;
242
+ "aria-posinset"?: number | undefined;
243
+ "aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined;
244
+ "aria-readonly"?: ("false" | "true" | boolean) | undefined;
245
+ "aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined;
246
+ "aria-required"?: ("false" | "true" | boolean) | undefined;
247
+ "aria-roledescription"?: string | undefined;
248
+ "aria-rowcount"?: number | undefined;
249
+ "aria-rowindex"?: number | undefined;
250
+ "aria-rowindextext"?: string | undefined;
251
+ "aria-rowspan"?: number | undefined;
252
+ "aria-selected"?: ("false" | "true" | boolean) | undefined;
253
+ "aria-setsize"?: number | undefined;
254
+ "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined;
255
+ "aria-valuemax"?: number | undefined;
256
+ "aria-valuemin"?: number | undefined;
257
+ "aria-valuenow"?: number | undefined;
258
+ "aria-valuetext"?: string | undefined;
259
+ defaultChecked?: boolean | undefined;
260
+ defaultValue?: string | number | readonly string[] | undefined;
261
+ suppressContentEditableWarning?: boolean | undefined;
262
+ suppressHydrationWarning?: boolean | undefined;
263
+ accessKey?: string | undefined;
264
+ autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {});
265
+ autoFocus?: boolean | undefined;
266
+ className?: string | undefined;
267
+ contentEditable?: ("false" | "true" | boolean) | "inherit" | "plaintext-only" | undefined;
268
+ contextMenu?: string | undefined;
269
+ dir?: string | undefined;
270
+ draggable?: ("false" | "true" | boolean) | undefined;
271
+ enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined;
272
+ hidden?: boolean | undefined;
273
+ id?: string | undefined;
274
+ lang?: string | undefined;
275
+ nonce?: string | undefined;
276
+ slot?: string | undefined;
277
+ spellCheck?: ("false" | "true" | boolean) | undefined;
278
+ style?: import("react").CSSProperties | undefined;
279
+ tabIndex?: number | undefined;
280
+ title?: string | undefined;
281
+ translate?: "yes" | "no" | undefined;
282
+ radioGroup?: string | undefined;
283
+ role?: import("react").AriaRole | undefined;
284
+ about?: string | undefined;
285
+ content?: string | undefined;
286
+ datatype?: string | undefined;
287
+ inlist?: any;
288
+ prefix?: string | undefined;
289
+ property?: string | undefined;
290
+ rel?: string | undefined;
291
+ resource?: string | undefined;
292
+ rev?: string | undefined;
293
+ typeof?: string | undefined;
294
+ vocab?: string | undefined;
295
+ autoCorrect?: string | undefined;
296
+ autoSave?: string | undefined;
297
+ color?: string | undefined;
298
+ itemProp?: string | undefined;
299
+ itemScope?: boolean | undefined;
300
+ itemType?: string | undefined;
301
+ itemID?: string | undefined;
302
+ itemRef?: string | undefined;
303
+ results?: number | undefined;
304
+ security?: string | undefined;
305
+ unselectable?: "on" | "off" | undefined;
306
+ popover?: "" | "auto" | "manual" | "hint" | undefined;
307
+ popoverTargetAction?: "toggle" | "show" | "hide" | undefined;
308
+ popoverTarget?: string | undefined;
309
+ inert?: boolean | undefined;
310
+ inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined;
311
+ is?: string | undefined;
312
+ exportparts?: string | undefined;
313
+ part?: string | undefined;
314
+ alt?: string | undefined;
315
+ crossOrigin?: "" | "anonymous" | "use-credentials" | undefined;
316
+ decoding?: "async" | "auto" | "sync" | undefined;
317
+ fetchPriority?: "high" | "low" | "auto" | undefined;
318
+ height?: number | string | undefined;
319
+ loading?: "eager" | "lazy" | undefined;
320
+ referrerPolicy?: import("react").HTMLAttributeReferrerPolicy | undefined;
321
+ sizes?: string | undefined;
322
+ src?: string | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_IMG_SRC_TYPES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_IMG_SRC_TYPES] | undefined;
323
+ srcSet?: string | undefined;
324
+ useMap?: string | undefined;
325
+ width?: number | string | undefined;
326
326
  }, HTMLElement>;
327
327
  export default Image;
328
328
  //# sourceMappingURL=image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/shims/image.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,GAAG;IAC7D,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,KAAK,EACX,WAAW,EAAE,YAAY,EACzB,WAAW,EAAE,YAAY,EACzB,GAAG,IAAI,EACR,EAAE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAEZ;AAED,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/shims/image.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,GAAG;IAC7D,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,KAAK,EACX,WAAW,EAAE,YAAY,EACzB,WAAW,EAAE,YAAY,EACzB,GAAG,IAAI,EACR,EAAE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAEZ;eAEc,KAAK"}
@@ -56,6 +56,101 @@ cache.invalidate({ tag: 'products' });
56
56
  cache.invalidate({ key: 'user:abc' });
57
57
  ```
58
58
 
59
+ ## `cache.component(Component, options)`
60
+
61
+ Wrap an async server component to cache its rendered RSC flight payload. The component renders once and subsequent requests splice the cached output into the live stream.
62
+
63
+ ```ts
64
+ import { cache } from '@timber-js/app/cache';
65
+
66
+ const PostBody = cache.component(
67
+ async function PostBody() {
68
+ const post = await getPost();
69
+ return <article>{post.content}</article>;
70
+ },
71
+ { prerender: true }
72
+ );
73
+ ```
74
+
75
+ `prerender` controls **when** the cache is populated. `ttl`/`tags` control **how it refreshes at runtime**. They are orthogonal — every combination is a meaningful tier:
76
+
77
+ ```tsx
78
+ // Static: rendered at build, changes only on redeploy
79
+ const BlogBody = cache.component(Body, { prerender: true });
80
+
81
+ // Cached: rendered on first request, refreshed by TTL/tags
82
+ const Pricing = cache.component(PricingTable, { ttl: 300, tags: ['pricing'] });
83
+
84
+ // ISR: rendered at build, refreshable without a redeploy
85
+ const DocsPage = cache.component(DocsBody, { prerender: true, ttl: 3600, tags: ['docs'] });
86
+ ```
87
+
88
+ ### `PrebuiltComponentOptions`
89
+
90
+ | Option | Type | Description |
91
+ | ------------------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------- |
92
+ | `prerender` | `boolean` | Populate at build time via `generateStaticSegmentParams` |
93
+ | `ttl` | `number` | Runtime freshness window in seconds |
94
+ | `tags` | `string[] \| (props) => string[]` | Tags for invalidation via `revalidateTag` |
95
+ | `staleWhileRevalidate` | `boolean` | Serve stale while a background re-render refreshes |
96
+ | `slots` | `string[]` | Prop names whose values are live React elements composed into the cached shell at request time |
97
+ | `fallbackOnMissingSegmentParams`| `boolean` | When `false`, a cache miss throws instead of rendering live (default: `true`) |
98
+
99
+ Only module-scope declarations are transformed at build time:
100
+
101
+ ```ts
102
+ // ✅ Transformed — eligible for prebuilding
103
+ const Shell = cache.component(MyComponent, { prerender: true });
104
+
105
+ // ❌ Not transformed — renders dynamically with a warning
106
+ export default function Page() {
107
+ const Cached = cache.component(Inner, { ttl: 60 }); // nested scope
108
+ }
109
+ ```
110
+
111
+ ### Tier Rules
112
+
113
+ | Declaration | Enumerated params | Unenumerated miss |
114
+ | ---------------------------- | ----------------------- | ------------------------------------ |
115
+ | `prerender` only | Built at deploy | Live render every time, never stored |
116
+ | `ttl`/`tags` only | — | Rendered + stored on first request |
117
+ | `prerender` + `ttl`/`tags` | Built at deploy | Rendered + stored on first request |
118
+
119
+ ### Slots
120
+
121
+ Cache a wrapper component's output as a shell with holes for live children:
122
+
123
+ ```tsx
124
+ const Shell = cache.component(ExpensiveWrapper, {
125
+ ttl: 3600,
126
+ tags: ['marketing'],
127
+ slots: ['children'],
128
+ });
129
+
130
+ // Request time: the shell is served from cache; children render live.
131
+ <Shell theme="dark"><LiveUserFeed /></Shell>
132
+ ```
133
+
134
+ Slot props are excluded from the cache key — two instances with the same data props share one shell entry. Slots are **runtime tier only**: `prerender` is ignored when `slots` is active because the build cannot enumerate children variants.
135
+
136
+ ### Route-Level Sugar
137
+
138
+ For "this whole route is static," export `prerender` from a page or layout instead of wrapping manually:
139
+
140
+ ```ts title="Fully static route"
141
+ export const prerender = true;
142
+ ```
143
+
144
+ ```ts title="Route-level ISR"
145
+ export const prerender = { ttl: 3600, tags: ['docs'] };
146
+ ```
147
+
148
+ This desugars to wrapping the page's default export in `cache.component(...)`. Layout `prerender` cascades to descendant pages unless overridden (most-specific-wins).
149
+
150
+ ### Nesting
151
+
152
+ When a cached component renders inside another cached component, the outer entry's lifetime wins. The inner's output is baked into the outer's payload as a flat copy — `revalidateTag` on the inner's tags does **not** refresh the embedded copy. The framework warns at capture time when the inner declares a refresh channel the outer cannot honor.
153
+
59
154
  ## `CacheHandler` Interface
60
155
 
61
156
  Implement this to plug in a custom cache backend.
@@ -100,15 +195,19 @@ interface RedisClient {
100
195
  srem(key: string, ...members: string[]): Promise<unknown>;
101
196
  smembers(key: string): Promise<string[]>;
102
197
  ttl(key: string): Promise<number>;
198
+ execMulti?(ops: RedisWriteOp[]): Promise<void>; // optional MULTI/EXEC
103
199
  }
104
200
  ```
105
201
 
106
202
  `expireNX` and `expireGT` map to Redis 7.0's `EXPIRE key seconds NX|GT` subcommands. `NX` sets the TTL only if none exists, `GT` extends only if greater. Both are used internally for atomic tag set TTL management. Separate methods ensure adapters cannot silently drop the mode. Requires **Redis 7.0+**.
107
203
 
204
+ `execMulti` is optional. When provided, `set()` executes tag-index writes and the data write atomically via `MULTI/EXEC`, preventing concurrent `invalidate({tag})` calls from interleaving. When absent, writes are sequential (correct but with a narrow self-healing race window).
205
+
108
206
  This is deliberately not the native shape of any client library — ioredis, node-redis, and @upstash/redis disagree on TTL argument shapes and method casing, and a contract that pattern-matches one client lets another silently write keys with no expiry. Wrap your client with one of these adapters:
109
207
 
110
208
  ```ts title="ioredis"
111
209
  import { RedisCacheHandler } from '@timber-js/app/cache';
210
+ import type { RedisWriteOp } from '@timber-js/app/cache';
112
211
  import Redis from 'ioredis';
113
212
 
114
213
  const redis = new Redis(process.env.REDIS_URL!);
@@ -122,6 +221,19 @@ const handler = new RedisCacheHandler({
122
221
  srem: (k, ...m) => redis.srem(k, ...m),
123
222
  smembers: (k) => redis.smembers(k),
124
223
  ttl: (k) => redis.ttl(k),
224
+ execMulti: async (ops) => {
225
+ const p = redis.multi();
226
+ for (const op of ops) {
227
+ switch (op.op) {
228
+ case 'set': p.set(op.key, op.value, 'EX', op.ttlSeconds); break;
229
+ case 'sadd': p.sadd(op.key, ...op.members); break;
230
+ case 'expireNX': p.expire(op.key, op.seconds, 'NX'); break;
231
+ case 'expireGT': p.expire(op.key, op.seconds, 'GT'); break;
232
+ case 'del': p.del(op.key); break;
233
+ }
234
+ }
235
+ await p.exec();
236
+ },
125
237
  });
126
238
  ```
127
239
 
@@ -17,8 +17,6 @@ interface TimberUserConfig {
17
17
  buildDir?: string;
18
18
  clientJavascript?: boolean | ClientJavascriptConfig;
19
19
  adapter?: TimberPlatformAdapter;
20
- cacheHandler?: CacheHandler;
21
- cdnPurge?: CdnPurgeHandler;
22
20
  serverTiming?: 'detailed' | 'total' | false;
23
21
  allowedOrigins?: string[];
24
22
  csrf?: boolean;
@@ -27,9 +25,6 @@ interface TimberUserConfig {
27
25
  uploadBodySize?: string;
28
26
  maxFields?: number;
29
27
  };
30
- actions?: {
31
- runMiddleware?: boolean;
32
- };
33
28
  forms?: {
34
29
  stripSensitiveFields?: boolean | readonly string[];
35
30
  };
@@ -119,13 +114,6 @@ adapter: cloudflare();
119
114
  adapter: nitro({ preset: 'node-server' });
120
115
  ```
121
116
 
122
- ### `cacheHandler`
123
-
124
- - **Type:** `CacheHandler`
125
- - **Default:** `MemoryCacheHandler`
126
-
127
- Cache backend for `timber.cache()`. See [Cache API](/docs/api-cache).
128
-
129
117
  ### `serverTiming`
130
118
 
131
119
  - **Type:** `'detailed' | 'total' | false`
@@ -159,13 +147,6 @@ limits: {
159
147
  }
160
148
  ```
161
149
 
162
- ### `actions.runMiddleware`
163
-
164
- - **Type:** `boolean`
165
- - **Default:** `true`
166
-
167
- Run `middleware.ts` on server action requests before dispatching. Keeps authentication, rate limiting, and other cross-cutting checks applied uniformly to page renders, route handlers, and server actions. Set to `false` only if your middleware would corrupt action submissions (e.g. it rewrites POST bodies) — you then own placing those checks inside every action.
168
-
169
150
  ### `forms.stripSensitiveFields`
170
151
 
171
152
  - **Type:** `boolean | string[]`
@@ -191,13 +172,6 @@ Two files claiming the same convention in one directory (e.g. `page.tsx` + `page
191
172
 
192
173
  Enable timber debug logging in production builds. When `true`, timber's own diagnostics (dev warnings, verbose logging) are active even in production mode. React stays in production mode — only timber's logs are affected. Can also be enabled at runtime via the `TIMBER_DEBUG` environment variable.
193
174
 
194
- ### `cdnPurge`
195
-
196
- - **Type:** `CdnPurgeHandler`
197
- - **Default:** none
198
-
199
- CDN purge handler called on `revalidateTag` / `cache.invalidate` to purge CDN-cached responses.
200
-
201
175
  ### `slowRequestMs`
202
176
 
203
177
  - **Type:** `number`
@@ -117,16 +117,132 @@ On serverless platforms (Cloudflare Workers, Lambda), the background refetch is
117
117
 
118
118
  When multiple concurrent requests miss the cache for the same key, only one executes the underlying function. The rest wait for the result. The result is written to the cache exactly once, by the execution that ran the function. This is built in — no opt-in needed.
119
119
 
120
+ ## Caching Components
121
+
122
+ `cache.data` caches a function's return value. `cache.component` caches a component's **rendered output** — the RSC flight payload — so re-renders skip the entire component tree:
123
+
124
+ ```tsx title="app/blog/[slug]/page.tsx"
125
+ import { cache } from '@timber-js/app/cache';
126
+ import { getSegmentParams } from '@timber-js/app/server';
127
+
128
+ const PostBody = cache.component(
129
+ async function PostBody() {
130
+ const { slug } = getSegmentParams();
131
+ const post = await getPost(slug);
132
+ return <article><h1>{post.title}</h1><PostContent content={post.content} /></article>;
133
+ },
134
+ { prerender: true }
135
+ );
136
+
137
+ export async function generateStaticSegmentParams() {
138
+ return allPosts.map((p) => ({ slug: p.slug }));
139
+ }
140
+
141
+ export default async function BlogPost() {
142
+ return <PostBody />;
143
+ }
144
+ ```
145
+
146
+ The same API gives you three tiers of freshness, controlled by two options:
147
+
148
+ ### Static (build-time only)
149
+
150
+ Rendered at build for each set of params from `generateStaticSegmentParams`. Changes only on redeploy. Request context (`getCookieJar()`, `getHeaders()`) is a build error.
151
+
152
+ ```tsx
153
+ const BlogBody = cache.component(Body, { prerender: true });
154
+ ```
155
+
156
+ ### Cached (runtime only)
157
+
158
+ Rendered on first request, stored with a TTL. Invalidatable by tag. May read request context (with a warning — be careful not to leak per-user data into a shared cache).
159
+
160
+ ```tsx
161
+ const Pricing = cache.component(PricingTable, {
162
+ ttl: 300,
163
+ tags: ['pricing'],
164
+ staleWhileRevalidate: true,
165
+ });
166
+ ```
167
+
168
+ ### ISR (build-seeded, runtime-refreshable)
169
+
170
+ Rendered at build, but refreshable via `revalidateTag` without a redeploy. Unenumerated params fill in on first request.
171
+
172
+ ```tsx
173
+ const DocsPage = cache.component(DocsBody, {
174
+ prerender: true,
175
+ ttl: 3600,
176
+ tags: ['docs'],
177
+ });
178
+ ```
179
+
180
+ ### Mixing Tiers on One Page
181
+
182
+ A page can combine all three — a dynamic shell with cached islands:
183
+
184
+ ```tsx title="app/blog/[slug]/page.tsx"
185
+ import { getCookieJar } from '@timber-js/app/server';
186
+
187
+ export default async function BlogPost() {
188
+ const user = await getUser(getCookieJar());
189
+ return (
190
+ <>
191
+ <Nav user={user} /> {/* dynamic — personalized */}
192
+ <PostBody /> {/* prerendered — from build artifact */}
193
+ <Suspense fallback={<CommentsSkeleton />}>
194
+ <Comments /> {/* dynamic — streams per-request */}
195
+ </Suspense>
196
+ </>
197
+ );
198
+ }
199
+ ```
200
+
201
+ ### Route-Level Shorthand
202
+
203
+ For fully static routes, export `prerender` instead of wrapping the component manually:
204
+
205
+ ```ts title="app/about/page.tsx"
206
+ export const prerender = true;
207
+ ```
208
+
209
+ This desugars to `cache.component(...)` around the page's default export. For dynamic routes (`[slug]`), pair it with `generateStaticSegmentParams` so the build knows which params to prerender:
210
+
211
+ ```ts title="app/docs/[slug]/page.tsx"
212
+ export const prerender = { ttl: 3600, tags: ['docs'] };
213
+
214
+ export async function generateStaticSegmentParams() {
215
+ return allDocs.map((d) => ({ slug: d.slug }));
216
+ }
217
+ ```
218
+
219
+ Layout `prerender` cascades to descendant pages unless a closer layout or page overrides it.
220
+
221
+ ### Slots: Dynamic Children Through a Cached Shell
222
+
223
+ When the expensive part is a wrapper (nav, sidebar, layout chrome) but its children are dynamic, use `slots` to cache the shell with holes for live content:
224
+
225
+ ```tsx
226
+ const Shell = cache.component(ExpensiveWrapper, {
227
+ ttl: 3600,
228
+ tags: ['marketing'],
229
+ slots: ['children'],
230
+ });
231
+
232
+ // The shell serves from cache; children render live per request.
233
+ <Shell theme="dark"><LiveUserFeed /></Shell>
234
+ ```
235
+
236
+ Slot props are excluded from the cache key. Slots are runtime tier only — `prerender` is ignored when `slots` is active.
237
+
120
238
  ## Cache Handlers
121
239
 
122
- The default handler is in-memory. Configure a different backend in `timber.config.ts`:
240
+ The default handler is in-memory. Configure a different backend in `timber.cache.ts`:
123
241
 
124
- ```ts title="timber.config.ts"
242
+ ```ts title="timber.cache.ts"
125
243
  import { MemoryCacheHandler } from '@timber-js/app/cache';
126
244
 
127
- export default {
128
- cacheHandler: new MemoryCacheHandler(),
129
- };
245
+ export default new MemoryCacheHandler();
130
246
  ```
131
247
 
132
248
  Implement the `CacheHandler` interface for Redis, Cloudflare KV, or any other backend. See the [Cache API Reference](/docs/api-cache).
@@ -108,7 +108,13 @@ export function NavItem({ href, children }: { href: string; children: React.Reac
108
108
 
109
109
  ## Segment Cache
110
110
 
111
- The client maintains a mirror of the server's segment tree. On navigation, only changed segments are re-fetched:
111
+ Opt-in via `clientSegmentCache: true` in `timber.config.ts`. When enabled, the client maintains a mirror of the server's segment tree. On navigation, only changed segments are re-fetched:
112
+
113
+ ```ts title="timber.config.ts"
114
+ export default {
115
+ clientSegmentCache: true,
116
+ };
117
+ ```
112
118
 
113
119
  ```
114
120
  /dashboard/settings → /dashboard/team
@@ -121,6 +127,8 @@ The client maintains a mirror of the server's segment tree. On navigation, only
121
127
 
122
128
  Sync layouts stay cached while mounted. Async layouts always re-render. Pages always re-render. Client component state in shared layouts is preserved — counters, form inputs, scroll positions survive navigation.
123
129
 
130
+ When disabled (the default), every client navigation gets a full RSC payload from the server. This is simpler and avoids edge cases with stale cached layouts, at the cost of slightly larger payloads on navigation.
131
+
124
132
  Back/forward navigation replays cached RSC payloads instantly — no server roundtrip.
125
133
 
126
134
  ## Scroll Restoration
@@ -81,16 +81,15 @@ export default {
81
81
 
82
82
  ## Cache Handler
83
83
 
84
- ```ts title="timber.config.ts"
84
+ Cache handler configuration lives in a separate `timber.cache.ts` file (not `timber.config.ts`). This keeps runtime cache instances separate from build-time config, preventing build dependencies from leaking into the server bundle.
85
+
86
+ ```ts title="timber.cache.ts"
85
87
  import { MemoryCacheHandler } from '@timber-js/app/cache';
86
88
 
87
- export default {
88
- output: 'server',
89
- cacheHandler: new MemoryCacheHandler(),
90
- };
89
+ export default new MemoryCacheHandler();
91
90
  ```
92
91
 
93
- The default is in-memory. See [Caching](/docs/caching) for Redis, KV, and custom handlers.
92
+ The default export is the `CacheHandler` instance. Export `cdnPurge` as a named export for CDN purge handlers. The default handler is in-memory. See [Caching](/docs/caching) for Redis, KV, and custom handlers.
94
93
 
95
94
  ## `clientJavascript`
96
95
 
@@ -129,8 +128,6 @@ Server actions still work — HTML forms submit natively via POST without JavaSc
129
128
  | `buildDir` | `string` | `'.timber/dist'` | Build output directory |
130
129
  | `clientJavascript` | `boolean \| object` | `true` | Control client-side JS |
131
130
  | `adapter` | `TimberPlatformAdapter` | — | Deployment adapter |
132
- | `cacheHandler` | `CacheHandler` | `MemoryCacheHandler` | Cache backend |
133
- | `cdnPurge` | `CdnPurgeHandler` | — | CDN purge on revalidation |
134
131
  | `serverTiming` | `'detailed' \| 'total' \| false` | `'detailed'` / `'total'` | Server-Timing header |
135
132
  | `allowedOrigins` | `string[]` | — | CORS / CSRF allowed origins |
136
133
  | `csrf` | `boolean` | `true` | CSRF protection |
@@ -148,6 +145,7 @@ Server actions still work — HTML forms submit natively via POST without JavaSc
148
145
  | `actionEncryption` | `object` | — | Server action bound args encryption |
149
146
  | `reactCompiler` | `boolean \| object` | `false` | React Compiler auto-memoization |
150
147
  | `sitemap` | `object` | — | Auto-generated sitemap.xml |
148
+ | `clientSegmentCache`| `boolean` | `false` | Opt-in client segment cache for partial nav |
151
149
  | `topLoader` | `object` | enabled | Navigation progress bar |
152
150
 
153
151
  For the full type definition, see the [Config API Reference](/docs/api-config).