@timber-js/app 0.2.0-alpha.34 → 0.2.0-alpha.36

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 (237) hide show
  1. package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
  2. package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
  3. package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
  4. package/dist/_chunks/{debug-B3Gypr3D.js → debug-ECi_61pb.js} +1 -1
  5. package/dist/_chunks/{debug-B3Gypr3D.js.map → debug-ECi_61pb.js.map} +1 -1
  6. package/dist/_chunks/define-cookie-w5GWm_bL.js +93 -0
  7. package/dist/_chunks/define-cookie-w5GWm_bL.js.map +1 -0
  8. package/dist/_chunks/error-boundary-TYEQJZ1-.js +211 -0
  9. package/dist/_chunks/error-boundary-TYEQJZ1-.js.map +1 -0
  10. package/dist/_chunks/{format-RyoGQL74.js → format-cX7wzEp2.js} +2 -2
  11. package/dist/_chunks/{format-RyoGQL74.js.map → format-cX7wzEp2.js.map} +1 -1
  12. package/dist/_chunks/{interception-BOoWmLUA.js → interception-D2djYaIm.js} +112 -77
  13. package/dist/_chunks/interception-D2djYaIm.js.map +1 -0
  14. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  15. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  16. package/dist/_chunks/{request-context-BQUC8PHn.js → request-context-CZz_T0Bc.js} +40 -71
  17. package/dist/_chunks/request-context-CZz_T0Bc.js.map +1 -0
  18. package/dist/_chunks/segment-context-Dpq2XOKg.js +34 -0
  19. package/dist/_chunks/segment-context-Dpq2XOKg.js.map +1 -0
  20. package/dist/_chunks/stale-reload-C0ValzG7.js +47 -0
  21. package/dist/_chunks/stale-reload-C0ValzG7.js.map +1 -0
  22. package/dist/_chunks/{tracing-CemImE6h.js → tracing-BPyIzIdu.js} +2 -2
  23. package/dist/_chunks/{tracing-CemImE6h.js.map → tracing-BPyIzIdu.js.map} +1 -1
  24. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-BvW0TKDn.js} +1 -1
  25. package/dist/_chunks/{use-query-states-D5KaffOK.js.map → use-query-states-BvW0TKDn.js.map} +1 -1
  26. package/dist/_chunks/wrappers-C1SN725w.js +331 -0
  27. package/dist/_chunks/wrappers-C1SN725w.js.map +1 -0
  28. package/dist/cache/index.js +1 -1
  29. package/dist/client/error-boundary.d.ts +10 -1
  30. package/dist/client/error-boundary.d.ts.map +1 -1
  31. package/dist/client/error-boundary.js +1 -125
  32. package/dist/client/index.d.ts +2 -2
  33. package/dist/client/index.d.ts.map +1 -1
  34. package/dist/client/index.js +193 -90
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/client/link.d.ts +8 -8
  37. package/dist/client/link.d.ts.map +1 -1
  38. package/dist/client/navigation-context.d.ts +2 -2
  39. package/dist/client/router.d.ts +25 -3
  40. package/dist/client/router.d.ts.map +1 -1
  41. package/dist/client/rsc-fetch.d.ts +23 -2
  42. package/dist/client/rsc-fetch.d.ts.map +1 -1
  43. package/dist/client/segment-cache.d.ts +1 -1
  44. package/dist/client/segment-cache.d.ts.map +1 -1
  45. package/dist/client/stale-reload.d.ts +15 -0
  46. package/dist/client/stale-reload.d.ts.map +1 -1
  47. package/dist/client/top-loader.d.ts +1 -1
  48. package/dist/client/top-loader.d.ts.map +1 -1
  49. package/dist/client/use-params.d.ts +2 -2
  50. package/dist/client/use-params.d.ts.map +1 -1
  51. package/dist/client/use-query-states.d.ts +1 -1
  52. package/dist/codec.d.ts +21 -0
  53. package/dist/codec.d.ts.map +1 -0
  54. package/dist/cookies/define-cookie.d.ts +33 -12
  55. package/dist/cookies/define-cookie.d.ts.map +1 -1
  56. package/dist/cookies/index.js +1 -81
  57. package/dist/index.d.ts +87 -12
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +346 -210
  60. package/dist/index.js.map +1 -1
  61. package/dist/params/define.d.ts +76 -0
  62. package/dist/params/define.d.ts.map +1 -0
  63. package/dist/params/index.d.ts +8 -0
  64. package/dist/params/index.d.ts.map +1 -0
  65. package/dist/params/index.js +104 -0
  66. package/dist/params/index.js.map +1 -0
  67. package/dist/plugins/adapter-build.d.ts.map +1 -1
  68. package/dist/plugins/build-manifest.d.ts.map +1 -1
  69. package/dist/plugins/client-chunks.d.ts +32 -0
  70. package/dist/plugins/client-chunks.d.ts.map +1 -0
  71. package/dist/plugins/entries.d.ts.map +1 -1
  72. package/dist/plugins/routing.d.ts.map +1 -1
  73. package/dist/plugins/server-bundle.d.ts.map +1 -1
  74. package/dist/plugins/static-build.d.ts.map +1 -1
  75. package/dist/routing/codegen.d.ts +2 -2
  76. package/dist/routing/codegen.d.ts.map +1 -1
  77. package/dist/routing/index.js +1 -1
  78. package/dist/routing/scanner.d.ts.map +1 -1
  79. package/dist/routing/status-file-lint.d.ts +2 -1
  80. package/dist/routing/status-file-lint.d.ts.map +1 -1
  81. package/dist/routing/types.d.ts +6 -4
  82. package/dist/routing/types.d.ts.map +1 -1
  83. package/dist/rsc-runtime/rsc.d.ts +1 -1
  84. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  85. package/dist/search-params/codecs.d.ts +1 -1
  86. package/dist/search-params/define.d.ts +153 -0
  87. package/dist/search-params/define.d.ts.map +1 -0
  88. package/dist/search-params/index.d.ts +4 -5
  89. package/dist/search-params/index.d.ts.map +1 -1
  90. package/dist/search-params/index.js +3 -474
  91. package/dist/search-params/registry.d.ts +1 -1
  92. package/dist/search-params/wrappers.d.ts +53 -0
  93. package/dist/search-params/wrappers.d.ts.map +1 -0
  94. package/dist/server/access-gate.d.ts +4 -0
  95. package/dist/server/access-gate.d.ts.map +1 -1
  96. package/dist/server/action-encryption.d.ts +76 -0
  97. package/dist/server/action-encryption.d.ts.map +1 -0
  98. package/dist/server/action-handler.d.ts.map +1 -1
  99. package/dist/server/als-registry.d.ts +4 -4
  100. package/dist/server/als-registry.d.ts.map +1 -1
  101. package/dist/server/build-manifest.d.ts +2 -2
  102. package/dist/server/deny-renderer.d.ts.map +1 -1
  103. package/dist/server/early-hints.d.ts +13 -5
  104. package/dist/server/early-hints.d.ts.map +1 -1
  105. package/dist/server/error-boundary-wrapper.d.ts +4 -0
  106. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  107. package/dist/server/flight-injection-state.d.ts +78 -0
  108. package/dist/server/flight-injection-state.d.ts.map +1 -0
  109. package/dist/server/flight-scripts.d.ts +39 -0
  110. package/dist/server/flight-scripts.d.ts.map +1 -0
  111. package/dist/server/form-data.d.ts +29 -0
  112. package/dist/server/form-data.d.ts.map +1 -1
  113. package/dist/server/html-injectors.d.ts +3 -9
  114. package/dist/server/html-injectors.d.ts.map +1 -1
  115. package/dist/server/index.d.ts +1 -1
  116. package/dist/server/index.d.ts.map +1 -1
  117. package/dist/server/index.js +1819 -1629
  118. package/dist/server/index.js.map +1 -1
  119. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  120. package/dist/server/pipeline.d.ts.map +1 -1
  121. package/dist/server/request-context.d.ts +28 -40
  122. package/dist/server/request-context.d.ts.map +1 -1
  123. package/dist/server/route-element-builder.d.ts +7 -0
  124. package/dist/server/route-element-builder.d.ts.map +1 -1
  125. package/dist/server/route-matcher.d.ts +2 -2
  126. package/dist/server/route-matcher.d.ts.map +1 -1
  127. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  128. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  129. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  130. package/dist/server/slot-resolver.d.ts.map +1 -1
  131. package/dist/server/ssr-entry.d.ts.map +1 -1
  132. package/dist/server/ssr-render.d.ts +3 -0
  133. package/dist/server/ssr-render.d.ts.map +1 -1
  134. package/dist/server/tree-builder.d.ts +12 -8
  135. package/dist/server/tree-builder.d.ts.map +1 -1
  136. package/dist/server/types.d.ts +1 -3
  137. package/dist/server/types.d.ts.map +1 -1
  138. package/dist/server/version-skew.d.ts +61 -0
  139. package/dist/server/version-skew.d.ts.map +1 -0
  140. package/dist/shims/navigation-client.d.ts +1 -1
  141. package/dist/shims/navigation-client.d.ts.map +1 -1
  142. package/dist/shims/navigation.d.ts +1 -1
  143. package/dist/shims/navigation.d.ts.map +1 -1
  144. package/dist/utils/state-machine.d.ts +80 -0
  145. package/dist/utils/state-machine.d.ts.map +1 -0
  146. package/package.json +12 -8
  147. package/src/client/browser-entry.ts +58 -25
  148. package/src/client/error-boundary.tsx +18 -1
  149. package/src/client/index.ts +9 -1
  150. package/src/client/link.tsx +9 -9
  151. package/src/client/navigation-context.ts +2 -2
  152. package/src/client/router.ts +102 -55
  153. package/src/client/rsc-fetch.ts +63 -2
  154. package/src/client/segment-cache.ts +1 -1
  155. package/src/client/stale-reload.ts +28 -0
  156. package/src/client/top-loader.tsx +2 -2
  157. package/src/client/use-params.ts +3 -3
  158. package/src/client/use-query-states.ts +1 -1
  159. package/src/codec.ts +21 -0
  160. package/src/cookies/define-cookie.ts +69 -18
  161. package/src/index.ts +255 -65
  162. package/src/params/define.ts +260 -0
  163. package/src/params/index.ts +28 -0
  164. package/src/plugins/adapter-build.ts +6 -0
  165. package/src/plugins/build-manifest.ts +11 -0
  166. package/src/plugins/client-chunks.ts +65 -0
  167. package/src/plugins/entries.ts +3 -6
  168. package/src/plugins/routing.ts +40 -14
  169. package/src/plugins/server-bundle.ts +32 -1
  170. package/src/plugins/shims.ts +1 -1
  171. package/src/plugins/static-build.ts +8 -4
  172. package/src/routing/codegen.ts +109 -88
  173. package/src/routing/scanner.ts +55 -6
  174. package/src/routing/status-file-lint.ts +2 -1
  175. package/src/routing/types.ts +7 -4
  176. package/src/rsc-runtime/rsc.ts +2 -0
  177. package/src/search-params/codecs.ts +1 -1
  178. package/src/search-params/define.ts +504 -0
  179. package/src/search-params/index.ts +12 -18
  180. package/src/search-params/registry.ts +1 -1
  181. package/src/search-params/wrappers.ts +85 -0
  182. package/src/server/access-gate.tsx +38 -8
  183. package/src/server/action-encryption.ts +144 -0
  184. package/src/server/action-handler.ts +16 -0
  185. package/src/server/als-registry.ts +4 -4
  186. package/src/server/build-manifest.ts +4 -4
  187. package/src/server/deny-renderer.ts +2 -1
  188. package/src/server/early-hints.ts +36 -15
  189. package/src/server/error-boundary-wrapper.ts +57 -14
  190. package/src/server/flight-injection-state.ts +152 -0
  191. package/src/server/flight-scripts.ts +59 -0
  192. package/src/server/form-data.ts +76 -0
  193. package/src/server/html-injectors.ts +50 -58
  194. package/src/server/index.ts +2 -4
  195. package/src/server/node-stream-transforms.ts +65 -54
  196. package/src/server/pipeline.ts +98 -26
  197. package/src/server/request-context.ts +49 -124
  198. package/src/server/route-element-builder.ts +102 -99
  199. package/src/server/route-matcher.ts +2 -2
  200. package/src/server/rsc-entry/error-renderer.ts +5 -3
  201. package/src/server/rsc-entry/index.ts +26 -11
  202. package/src/server/rsc-entry/rsc-payload.ts +2 -2
  203. package/src/server/rsc-entry/ssr-renderer.ts +13 -5
  204. package/src/server/slot-resolver.ts +204 -206
  205. package/src/server/ssr-entry.ts +3 -1
  206. package/src/server/ssr-render.ts +3 -0
  207. package/src/server/tree-builder.ts +84 -48
  208. package/src/server/types.ts +1 -3
  209. package/src/server/version-skew.ts +104 -0
  210. package/src/shims/navigation-client.ts +1 -1
  211. package/src/shims/navigation.ts +1 -1
  212. package/src/utils/state-machine.ts +111 -0
  213. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  214. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  215. package/dist/_chunks/request-context-BQUC8PHn.js.map +0 -1
  216. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  217. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  218. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  219. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  220. package/dist/client/error-boundary.js.map +0 -1
  221. package/dist/cookies/index.js.map +0 -1
  222. package/dist/plugins/dynamic-transform.d.ts +0 -72
  223. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  224. package/dist/search-params/analyze.d.ts +0 -54
  225. package/dist/search-params/analyze.d.ts.map +0 -1
  226. package/dist/search-params/builtin-codecs.d.ts +0 -105
  227. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  228. package/dist/search-params/create.d.ts +0 -106
  229. package/dist/search-params/create.d.ts.map +0 -1
  230. package/dist/search-params/index.js.map +0 -1
  231. package/dist/server/prerender.d.ts +0 -77
  232. package/dist/server/prerender.d.ts.map +0 -1
  233. package/src/plugins/dynamic-transform.ts +0 -161
  234. package/src/search-params/analyze.ts +0 -192
  235. package/src/search-params/builtin-codecs.ts +0 -228
  236. package/src/search-params/create.ts +0 -321
  237. package/src/server/prerender.ts +0 -139
package/dist/index.js CHANGED
@@ -1,38 +1,16 @@
1
- import { r as setViteServer, t as formatSize } from "./_chunks/format-RyoGQL74.js";
2
- import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-BOoWmLUA.js";
1
+ import { r as __toESM, t as __commonJSMin } from "./_chunks/chunk-DYhsFzuS.js";
2
+ import { r as setViteServer, t as formatSize } from "./_chunks/format-cX7wzEp2.js";
3
+ import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-D2djYaIm.js";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
5
  import { dirname, extname, join, normalize, resolve } from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { createRequire } from "node:module";
7
- import react from "@vitejs/plugin-react";
7
+ import react, { reactCompilerPreset } from "@vitejs/plugin-react";
8
8
  import { constants, createGzip, gzipSync } from "node:zlib";
9
9
  import { Readable } from "node:stream";
10
+ import { fileURLToPath } from "node:url";
10
11
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
11
- import { createHash } from "node:crypto";
12
+ import { createHash, randomUUID } from "node:crypto";
12
13
  import { performance as performance$1 } from "node:perf_hooks";
13
- //#region \0rolldown/runtime.js
14
- var __create = Object.create;
15
- var __defProp = Object.defineProperty;
16
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
17
- var __getOwnPropNames = Object.getOwnPropertyNames;
18
- var __getProtoOf = Object.getPrototypeOf;
19
- var __hasOwnProp = Object.prototype.hasOwnProperty;
20
- var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
21
- var __copyProps = (to, from, except, desc) => {
22
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
23
- key = keys[i];
24
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
25
- get: ((k) => from[k]).bind(null, key),
26
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
27
- });
28
- }
29
- return to;
30
- };
31
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
32
- value: mod,
33
- enumerable: true
34
- }) : target, mod));
35
- //#endregion
36
14
  //#region ../../node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.mjs
37
15
  var astralIdentifierCodes = [
38
16
  509,
@@ -11928,7 +11906,6 @@ function stripRootPrefix(id, root) {
11928
11906
  * Serializes output mode and feature flags for runtime consumption.
11929
11907
  */
11930
11908
  function generateConfigModule(ctx) {
11931
- const cookieSecrets = ctx.config.cookies?.secrets ?? (ctx.config.cookies?.secret ? [ctx.config.cookies.secret] : void 0);
11932
11909
  const runtimeConfig = {
11933
11910
  output: ctx.config.output ?? "server",
11934
11911
  csrf: ctx.config.csrf ?? true,
@@ -11937,10 +11914,10 @@ function generateConfigModule(ctx) {
11937
11914
  dev: ctx.dev ?? false,
11938
11915
  slowPhaseMs: ctx.config.dev?.slowPhaseMs ?? 200,
11939
11916
  slowRequestMs: ctx.config.slowRequestMs ?? 3e3,
11940
- cookieSecrets,
11941
11917
  topLoader: ctx.config.topLoader,
11942
11918
  debug: ctx.config.debug ?? false,
11943
- serverTiming: ctx.config.serverTiming
11919
+ serverTiming: ctx.config.serverTiming,
11920
+ deploymentId: ctx.deploymentId ?? null
11944
11921
  };
11945
11922
  return [
11946
11923
  "// Auto-generated runtime config — do not edit.",
@@ -12163,7 +12140,8 @@ var CLIENT_REQUIRED_EXTENSIONS = new Set([
12163
12140
  * that are missing it.
12164
12141
  *
12165
12142
  * MDX and JSON status files are excluded — MDX files are server components
12166
- * by design, and JSON files are data, not components.
12143
+ * by design (pre-rendered as elements via fallbackElement, see TIM-503),
12144
+ * and JSON files are data, not components.
12167
12145
  */
12168
12146
  function lintStatusFileDirectives(tree) {
12169
12147
  const warnings = [];
@@ -12208,7 +12186,7 @@ var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
12208
12186
  /**
12209
12187
  * File convention names we track for changes that require manifest regeneration.
12210
12188
  */
12211
- var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied|search-params|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
12189
+ var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|default|denied|\d{3}|[45]xx|not-found|forbidden|unauthorized|sitemap|robots|manifest|favicon|icon|opengraph-image|twitter-image|apple-icon)\./;
12212
12190
  /**
12213
12191
  * Create the timber-routing Vite plugin.
12214
12192
  *
@@ -12283,14 +12261,40 @@ function timberRouting(ctx) {
12283
12261
  configureServer(devServer) {
12284
12262
  rescan();
12285
12263
  devServer.watcher.add(ctx.appDir);
12286
- const handleFileChange = (filePath) => {
12264
+ /** Snapshot of the last generated manifest, used to detect structural changes. */
12265
+ let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12266
+ /**
12267
+ * Handle a route-significant file being added or removed.
12268
+ * Always triggers a full-reload since the route tree structure changed.
12269
+ */
12270
+ const handleStructuralChange = (filePath) => {
12287
12271
  if (!filePath.startsWith(ctx.appDir)) return;
12288
12272
  if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
12289
12273
  rescan();
12274
+ lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12290
12275
  invalidateManifest(devServer);
12291
12276
  };
12292
- devServer.watcher.on("add", handleFileChange);
12293
- devServer.watcher.on("unlink", handleFileChange);
12277
+ /**
12278
+ * Handle a route file's content changing.
12279
+ *
12280
+ * Most content edits (JSX changes, fixing typos) don't affect route
12281
+ * metadata — Vite's React Fast Refresh handles those via normal HMR.
12282
+ * Only rescan and full-reload when route metadata actually changed
12283
+ * (e.g., searchParams export added/removed, metadata export changed).
12284
+ */
12285
+ const handleContentChange = (filePath) => {
12286
+ if (!filePath.startsWith(ctx.appDir)) return;
12287
+ if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
12288
+ rescan();
12289
+ const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree) : "";
12290
+ if (newManifest !== lastManifest) {
12291
+ lastManifest = newManifest;
12292
+ invalidateManifest(devServer);
12293
+ }
12294
+ };
12295
+ devServer.watcher.on("add", handleStructuralChange);
12296
+ devServer.watcher.on("unlink", handleStructuralChange);
12297
+ devServer.watcher.on("change", handleContentChange);
12294
12298
  }
12295
12299
  };
12296
12300
  }
@@ -12371,10 +12375,6 @@ function generateManifestModule(tree) {
12371
12375
  const v = addImport(node.denied);
12372
12376
  parts.push(`${nextIndent}denied: { load: ${v}, filePath: ${JSON.stringify(node.denied.filePath)} },`);
12373
12377
  }
12374
- if (node.searchParams) {
12375
- const v = addImport(node.searchParams);
12376
- parts.push(`${nextIndent}searchParams: { load: ${v}, filePath: ${JSON.stringify(node.searchParams.filePath)} },`);
12377
- }
12378
12378
  if (node.statusFiles && node.statusFiles.size > 0) {
12379
12379
  const statusEntries = [];
12380
12380
  for (const [code, file] of node.statusFiles) {
@@ -13877,16 +13877,14 @@ function validateStaticMode(code, fileId, options) {
13877
13877
  * - transform: Validates source files for static mode violations
13878
13878
  */
13879
13879
  function timberStaticBuild(ctx) {
13880
- const isStatic = ctx.config.output === "static";
13881
- const clientJavascriptDisabled = ctx.clientJavascript.disabled;
13882
13880
  return {
13883
13881
  name: "timber-static-build",
13884
13882
  transform(code, id) {
13885
- if (!isStatic) return null;
13883
+ if (!(ctx.config.output === "static")) return null;
13886
13884
  if (id.includes("node_modules")) return null;
13887
13885
  if (!id.includes("/app/") && !id.startsWith("app/")) return null;
13888
13886
  if (!/\.[jt]sx?$/.test(id)) return null;
13889
- const errors = validateStaticMode(code, id, { clientJavascriptDisabled });
13887
+ const errors = validateStaticMode(code, id, { clientJavascriptDisabled: ctx.clientJavascript.disabled });
13890
13888
  if (errors.length > 0) {
13891
13889
  const messages = errors.map((e) => `[timber] Static mode error in ${e.file}${e.line ? `:${e.line}` : ""}: ${e.message}`);
13892
13890
  this.error(messages.join("\n\n"));
@@ -13896,95 +13894,6 @@ function timberStaticBuild(ctx) {
13896
13894
  };
13897
13895
  }
13898
13896
  //#endregion
13899
- //#region src/plugins/dynamic-transform.ts
13900
- /**
13901
- * Quick check: does this source file contain 'use dynamic' anywhere?
13902
- * Used as a fast bail-out before doing expensive AST parsing.
13903
- */
13904
- function containsUseDynamic(code) {
13905
- return containsDirective(code, "use dynamic");
13906
- }
13907
- /**
13908
- * Find function declarations/expressions containing 'use dynamic' and
13909
- * transform them into markDynamic() calls.
13910
- *
13911
- * Input:
13912
- * ```tsx
13913
- * export default async function AddToCartButton({ productId }) {
13914
- * 'use dynamic'
13915
- * const user = await getUser()
13916
- * return <button>Add to cart</button>
13917
- * }
13918
- * ```
13919
- *
13920
- * Output:
13921
- * ```tsx
13922
- * import { markDynamic as __markDynamic } from '@timber-js/app/runtime';
13923
- * export default async function AddToCartButton({ productId }) {
13924
- * __markDynamic();
13925
- * const user = await getUser()
13926
- * return <button>Add to cart</button>
13927
- * }
13928
- * ```
13929
- *
13930
- * The markDynamic() call registers the component boundary as dynamic
13931
- * at render time. The pre-render pass uses this to know which subtrees
13932
- * to skip and leave as holes for per-request rendering.
13933
- */
13934
- function transformUseDynamic(code) {
13935
- if (!containsUseDynamic(code)) return null;
13936
- const functions = findFunctionsWithDirective(code, "use dynamic");
13937
- if (functions.length === 0) return null;
13938
- let result = code;
13939
- for (const fn of functions) {
13940
- const cleanBody = fn.bodyContent.replace(/['"]use dynamic['"];?/, "__markDynamic();");
13941
- result = result.slice(0, fn.bodyStart) + cleanBody + result.slice(fn.bodyEnd);
13942
- }
13943
- result = `import { markDynamic as __markDynamic } from '@timber-js/app/runtime';\n` + result;
13944
- return {
13945
- code: result,
13946
- map: null
13947
- };
13948
- }
13949
- /**
13950
- * In `output: 'static'` mode, `'use dynamic'` is a build error.
13951
- * Static mode renders everything at build time — there is no per-request
13952
- * rendering to opt into.
13953
- */
13954
- function validateNoDynamicInStaticMode(code) {
13955
- if (!containsUseDynamic(code)) return null;
13956
- const functions = findFunctionsWithDirective(code, "use dynamic");
13957
- if (functions.length === 0) return null;
13958
- return {
13959
- message: "'use dynamic' cannot be used in static mode (output: 'static'). Static mode renders all content at build time — there is no per-request rendering. Remove the directive or switch to output: 'server'.",
13960
- line: functions[functions.length - 1].directiveLine
13961
- };
13962
- }
13963
- /**
13964
- * Create the timber-dynamic-transform Vite plugin.
13965
- *
13966
- * In server mode: transforms 'use dynamic' into markDynamic() calls.
13967
- * In static mode: rejects 'use dynamic' as a build error.
13968
- */
13969
- function timberDynamicTransform(ctx) {
13970
- const isStatic = ctx.config.output === "static";
13971
- return {
13972
- name: "timber-dynamic-transform",
13973
- transform(code, id) {
13974
- if (id.includes("node_modules")) return null;
13975
- if (!id.includes("/app/") && !id.startsWith("app/")) return null;
13976
- if (!/\.[jt]sx?$/.test(id)) return null;
13977
- if (!containsUseDynamic(code)) return null;
13978
- if (isStatic) {
13979
- const error = validateNoDynamicInStaticMode(code);
13980
- if (error) this.error(`[timber] Static mode error in ${id}${error.line ? `:${error.line}` : ""}: ${error.message}`);
13981
- return null;
13982
- }
13983
- return transformUseDynamic(code);
13984
- }
13985
- };
13986
- }
13987
- //#endregion
13988
13897
  //#region src/plugins/server-action-exports.ts
13989
13898
  var jsxParser = Parser.extend((0, import_acorn_jsx.default)());
13990
13899
  /**
@@ -14124,6 +14033,23 @@ function rewriteServerActionExportsFallback(code) {
14124
14033
  }
14125
14034
  //#endregion
14126
14035
  //#region src/plugins/build-manifest.ts
14036
+ /**
14037
+ * timber-build-manifest — Vite sub-plugin for build asset manifest generation.
14038
+ *
14039
+ * Provides `virtual:timber-build-manifest` which exports a BuildManifest
14040
+ * mapping route segment file paths to their CSS, JS, and modulepreload
14041
+ * output chunks.
14042
+ *
14043
+ * - Dev mode: exports an empty manifest (Vite HMR handles CSS/JS).
14044
+ * - Build mode: virtual module reads from globalThis.__TIMBER_BUILD_MANIFEST__
14045
+ * at runtime. The actual manifest data is injected by the adapter via a
14046
+ * _timber-manifest-init.js module that runs before the RSC handler.
14047
+ *
14048
+ * The generateBundle hook (client env only) extracts CSS/JS/modulepreload
14049
+ * data from the Rollup bundle and populates ctx.buildManifest.
14050
+ *
14051
+ * Design docs: 18-build-system.md §"Build Manifest", 02-rendering-pipeline.md §"Early Hints"
14052
+ */
14127
14053
  var VIRTUAL_MODULE_ID = "virtual:timber-build-manifest";
14128
14054
  var RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
14129
14055
  /**
@@ -14199,6 +14125,7 @@ function timberBuildManifest(ctx) {
14199
14125
  configResolved(config) {
14200
14126
  resolvedBase = config.base;
14201
14127
  isDev = config.command === "serve";
14128
+ if (!isDev && !ctx.deploymentId) ctx.deploymentId = randomUUID().replace(/-/g, "").slice(0, 16);
14202
14129
  },
14203
14130
  resolveId(id) {
14204
14131
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
@@ -14469,55 +14396,115 @@ function timberChunks() {
14469
14396
  return { name: "timber-chunks" };
14470
14397
  }
14471
14398
  //#endregion
14399
+ //#region src/plugins/client-chunks.ts
14400
+ /**
14401
+ * Client chunk grouping strategy for @vitejs/plugin-rsc.
14402
+ *
14403
+ * Groups client reference modules by layout boundary to balance route-scoped
14404
+ * code splitting with HTTP request count. A constant group name would collapse
14405
+ * all routes into one chunk (every page downloads every client component).
14406
+ * Per-serverChunk grouping creates many sub-500B files. Layout-boundary
14407
+ * grouping is the middle ground.
14408
+ *
14409
+ * See design/27-chunking-strategy.md, TIM-440, TIM-499.
14410
+ */
14411
+ /**
14412
+ * Derive a chunk group name for a client reference module.
14413
+ *
14414
+ * Groups by the first non-group route segment under appDir so that all
14415
+ * client components belonging to the same layout boundary land in one
14416
+ * chunk. For example:
14417
+ * - `facade:app/dashboard/settings/page.tsx` → `"client-dashboard"`
14418
+ * - `facade:app/(group-a)/group-page-a/page.tsx` → `"client-group-page-a"`
14419
+ * - `facade:app/layout.tsx` (root layout) → `"client-shared"`
14420
+ * - `shared:...` (shared across chunks) → `"client-shared"`
14421
+ *
14422
+ * This balances route-scoped code splitting with HTTP request count:
14423
+ * fewer chunks than per-serverChunk, but still avoids downloading every
14424
+ * client component on every page.
14425
+ */
14426
+ function clientChunkGroup(meta, appDir) {
14427
+ const { normalizedId, serverChunk } = meta;
14428
+ if (serverChunk.startsWith("shared:")) return "client-shared";
14429
+ const relPath = normalizedId.replace(/\\/g, "/");
14430
+ const appDirName = appDir.replace(/\\/g, "/").split("/").pop() || "app";
14431
+ const appIdx = relPath.indexOf(appDirName + "/");
14432
+ if (appIdx === -1) return "client-shared";
14433
+ const segments = relPath.slice(appIdx + appDirName.length + 1).split("/");
14434
+ for (const seg of segments) {
14435
+ if (seg.includes(".")) break;
14436
+ if (seg.startsWith("(") && seg.endsWith(")")) continue;
14437
+ return `client-${seg}`;
14438
+ }
14439
+ return "client-shared";
14440
+ }
14441
+ //#endregion
14472
14442
  //#region src/plugins/server-bundle.ts
14473
14443
  function timberServerBundle() {
14474
- return [{
14475
- name: "timber-server-bundle",
14476
- config(_cfg, { command }) {
14477
- if (command === "serve") return { environments: {
14478
- rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
14479
- ssr: { resolve: { noExternal: [
14480
- "server-only",
14481
- "client-only",
14482
- "nuqs"
14483
- ] } }
14484
- } };
14485
- const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
14486
- return {
14487
- ssr: { target: "webworker" },
14488
- environments: {
14489
- rsc: {
14490
- resolve: { noExternal: true },
14491
- define: serverDefine
14492
- },
14493
- ssr: {
14494
- resolve: { noExternal: true },
14495
- define: serverDefine
14444
+ return [
14445
+ {
14446
+ name: "timber-server-bundle",
14447
+ config(_cfg, { command }) {
14448
+ if (command === "serve") return { environments: {
14449
+ rsc: { resolve: { noExternal: ["server-only", "client-only"] } },
14450
+ ssr: { resolve: { noExternal: [
14451
+ "server-only",
14452
+ "client-only",
14453
+ "nuqs"
14454
+ ] } }
14455
+ } };
14456
+ const serverDefine = { "process.env.NODE_ENV": JSON.stringify("production") };
14457
+ return {
14458
+ ssr: { target: "webworker" },
14459
+ environments: {
14460
+ rsc: {
14461
+ resolve: { noExternal: true },
14462
+ define: serverDefine
14463
+ },
14464
+ ssr: {
14465
+ resolve: { noExternal: true },
14466
+ define: serverDefine
14467
+ }
14496
14468
  }
14497
- }
14498
- };
14499
- }
14500
- }, {
14501
- name: "timber-esm-init-fix",
14502
- applyToEnvironment(environment) {
14503
- return environment.name === "rsc" || environment.name === "ssr";
14469
+ };
14470
+ }
14504
14471
  },
14505
- renderChunk(code) {
14506
- const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
14507
- if (!code.includes(lazy)) return null;
14508
- const eager = [
14509
- "var __esmMin = (fn, res) => {",
14510
- " var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
14511
- " l();",
14512
- " return l;",
14513
- "};"
14514
- ].join(" ");
14515
- return {
14516
- code: code.replace(lazy, eager),
14517
- map: null
14518
- };
14472
+ {
14473
+ name: "timber-esm-init-fix",
14474
+ applyToEnvironment(environment) {
14475
+ return environment.name === "rsc" || environment.name === "ssr";
14476
+ },
14477
+ renderChunk(code) {
14478
+ const lazy = "var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);";
14479
+ if (!code.includes(lazy)) return null;
14480
+ const eager = [
14481
+ "var __esmMin = (fn, res) => {",
14482
+ " var l = () => { if (fn) { var f = fn; try { res = f(); fn = 0; } catch(e) {} } return res; };",
14483
+ " l();",
14484
+ " return l;",
14485
+ "};"
14486
+ ].join(" ");
14487
+ return {
14488
+ code: code.replace(lazy, eager),
14489
+ map: null
14490
+ };
14491
+ }
14492
+ },
14493
+ {
14494
+ name: "timber-create-require-fix",
14495
+ applyToEnvironment(environment) {
14496
+ return environment.name === "rsc" || environment.name === "ssr";
14497
+ },
14498
+ renderChunk(code) {
14499
+ const pattern = "createRequire(import.meta.url)";
14500
+ if (!code.includes(pattern)) return null;
14501
+ return {
14502
+ code: code.replace(pattern, "createRequire(import.meta.url || \"file:///app\")"),
14503
+ map: null
14504
+ };
14505
+ }
14519
14506
  }
14520
- }];
14507
+ ];
14521
14508
  }
14522
14509
  //#endregion
14523
14510
  //#region src/plugins/adapter-build.ts
@@ -14539,6 +14526,7 @@ function timberAdapterBuild(ctx) {
14539
14526
  modulepreload: {}
14540
14527
  } : ctx.buildManifest;
14541
14528
  manifestInit = `globalThis.__TIMBER_BUILD_MANIFEST__ = ${JSON.stringify(manifest)};\n`;
14529
+ if (ctx.deploymentId) manifestInit += `globalThis.__TIMBER_DEPLOYMENT_ID__ = ${JSON.stringify(ctx.deploymentId)};\n`;
14542
14530
  }
14543
14531
  if (ctx.clientJavascript.disabled) await stripJsFromRscAssetsManifests(buildDir);
14544
14532
  const adapterConfig = {
@@ -14895,6 +14883,65 @@ function createNoopTimer() {
14895
14883
  };
14896
14884
  }
14897
14885
  //#endregion
14886
+ //#region src/server/action-encryption.ts
14887
+ /**
14888
+ * Regex for safe `defineEncryptionKey` expressions.
14889
+ *
14890
+ * The RSC plugin inlines this expression verbatim into generated JavaScript.
14891
+ * We restrict it to `process.env.<UPPER_SNAKE_CASE>` to prevent code injection.
14892
+ * See "Known Security Considerations" at the top of this file.
14893
+ */
14894
+ var SAFE_KEY_EXPR = /^process\.env\.[A-Z_][A-Z0-9_]*$/;
14895
+ /**
14896
+ * Build the `defineEncryptionKey` expression for the RSC plugin.
14897
+ *
14898
+ * The RSC plugin accepts a JavaScript expression string that will be
14899
+ * inlined into the encryption runtime module. At runtime, this expression
14900
+ * must evaluate to the base64-encoded encryption key.
14901
+ *
14902
+ * Priority:
14903
+ * 1. `TIMBER_ACTIONS_ENCRYPTION_KEY` env var (for cross-build key sharing
14904
+ * in rolling/blue-green deployments)
14905
+ * 2. Auto-generated at build time (RSC plugin default — embedded in bundle,
14906
+ * consistent across all instances of the same build)
14907
+ *
14908
+ * For env var keys, we generate a runtime expression that reads the env var.
14909
+ * For auto-generated keys, we return undefined and let the RSC plugin handle it.
14910
+ */
14911
+ function resolveEncryptionKeyExpression() {
14912
+ const envKey = process.env.TIMBER_ACTIONS_ENCRYPTION_KEY;
14913
+ if (envKey) {
14914
+ validateKeyFormat(envKey);
14915
+ const expr = "process.env.TIMBER_ACTIONS_ENCRYPTION_KEY";
14916
+ if (!SAFE_KEY_EXPR.test(expr)) throw new Error(`Unsafe encryption key expression: ${expr}`);
14917
+ return expr;
14918
+ }
14919
+ }
14920
+ /**
14921
+ * Determine whether action encryption should be enabled.
14922
+ *
14923
+ * Encryption is always enabled in production. In dev mode, it's enabled
14924
+ * by default but can be disabled via config for debugging.
14925
+ */
14926
+ function shouldEnableEncryption(isDev, config) {
14927
+ if (!isDev) return true;
14928
+ if (config?.disableInDev) return false;
14929
+ return true;
14930
+ }
14931
+ /**
14932
+ * Validate that a key string is a valid base64-encoded 256-bit key.
14933
+ * Throws a descriptive error if the key is malformed.
14934
+ */
14935
+ function validateKeyFormat(key) {
14936
+ try {
14937
+ const bytes = atob(key).length;
14938
+ if (bytes !== 32) throw new Error(`TIMBER_ACTIONS_ENCRYPTION_KEY must be a base64-encoded 256-bit (32-byte) key. Got ${bytes} bytes. Generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`);
14939
+ } catch (error) {
14940
+ if (error instanceof Error && error.message.includes("TIMBER_ACTIONS_ENCRYPTION_KEY")) throw error;
14941
+ throw new Error("TIMBER_ACTIONS_ENCRYPTION_KEY is not valid base64. Generate a key with: node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
14942
+ }
14943
+ }
14944
+ //#endregion
14898
14945
  //#region src/index.ts
14899
14946
  /**
14900
14947
  * Resolve `clientJavascript` into a fully resolved config.
@@ -14937,10 +14984,7 @@ function resolveAppDir(root, configAppDir) {
14937
14984
  }
14938
14985
  function createPluginContext(config, root) {
14939
14986
  const projectRoot = root ?? process.cwd();
14940
- const resolvedConfig = {
14941
- output: "server",
14942
- ...config
14943
- };
14987
+ const resolvedConfig = { ...config };
14944
14988
  return {
14945
14989
  config: resolvedConfig,
14946
14990
  clientJavascript: resolveClientJavascript(resolvedConfig),
@@ -14949,22 +14993,28 @@ function createPluginContext(config, root) {
14949
14993
  root: projectRoot,
14950
14994
  dev: false,
14951
14995
  buildManifest: null,
14996
+ deploymentId: null,
14952
14997
  timer: createStartupTimer()
14953
14998
  };
14954
14999
  }
14955
15000
  /**
14956
15001
  * Load timber.config.ts (or .js, .mjs) from the project root.
14957
15002
  * Returns the config object or null if no config file is found.
15003
+ *
15004
+ * Uses require() which works for ESM modules on Node 22.12+.
15005
+ * This keeps timber() synchronous — no async config loading needed.
14958
15006
  */
14959
- async function loadTimberConfigFile(root) {
14960
- for (const name of [
15007
+ function loadTimberConfigFile(root) {
15008
+ const configNames = [
14961
15009
  "timber.config.ts",
14962
15010
  "timber.config.js",
14963
15011
  "timber.config.mjs"
14964
- ]) {
15012
+ ];
15013
+ const req = createRequire(join(root, "package.json"));
15014
+ for (const name of configNames) {
14965
15015
  const configPath = join(root, name);
14966
15016
  if (existsSync(configPath)) {
14967
- const mod = await import(pathToFileURL(configPath).href);
15017
+ const mod = req(configPath);
14968
15018
  return mod.default ?? mod;
14969
15019
  }
14970
15020
  }
@@ -14979,10 +15029,7 @@ async function loadTimberConfigFile(root) {
14979
15029
  */
14980
15030
  function warnConfigConflicts(inline, fileConfig) {
14981
15031
  const conflicts = [];
14982
- for (const key of Object.keys(fileConfig)) {
14983
- if (key === "output") continue;
14984
- if (key in inline && inline[key] !== void 0) conflicts.push(key);
14985
- }
15032
+ for (const key of Object.keys(fileConfig)) if (key in inline && inline[key] !== void 0) conflicts.push(key);
14986
15033
  if (conflicts.length > 0) console.warn(`[timber] Config conflict: ${conflicts.map((k) => `"${k}"`).join(", ")} set in both vite.config.ts (inline) and timber.config.ts. Move all config to timber.config.ts to avoid confusion. The inline value from vite.config.ts will be used.`);
14987
15034
  return conflicts;
14988
15035
  }
@@ -15010,21 +15057,92 @@ function mergeFileConfig(ctx, fileConfig) {
15010
15057
  } } : {}
15011
15058
  };
15012
15059
  }
15060
+ /**
15061
+ * Resolve the React Compiler plugin via @rolldown/plugin-babel.
15062
+ *
15063
+ * Uses the `reactCompilerPreset` from @vitejs/plugin-react, which:
15064
+ * - Uses Babel ONLY for the compiler pass (OXC handles JSX)
15065
+ * - Automatically scopes to client environment via applyToEnvironmentHook
15066
+ * - Uses react/compiler-runtime built into React 19
15067
+ *
15068
+ * @rolldown/plugin-babel and babel-plugin-react-compiler are optional peer deps.
15069
+ * If either is missing, require() fails with a clear error message.
15070
+ */
15071
+ function resolveReactCompilerPlugin(config, req) {
15072
+ let babel;
15073
+ try {
15074
+ babel = req("@rolldown/plugin-babel");
15075
+ } catch {
15076
+ throw new Error("[timber] reactCompiler requires @rolldown/plugin-babel. Install it: pnpm add -D @rolldown/plugin-babel babel-plugin-react-compiler");
15077
+ }
15078
+ const options = typeof config === "object" ? config : {};
15079
+ return (babel.default ?? babel)({ presets: [reactCompilerPreset(options)] });
15080
+ }
15081
+ /**
15082
+ * Build the options object for @vitejs/plugin-rsc.
15083
+ *
15084
+ * Uses a getter for `enableActionEncryption` so the RSC plugin reads
15085
+ * the value lazily — after ctx.dev is set in configResolved. This lets
15086
+ * `actionEncryption.disableInDev` work correctly even though the RSC
15087
+ * plugin is created before Vite resolves the command.
15088
+ */
15089
+ function createRscOptions(ctx, encryptionKeyExpr) {
15090
+ const options = {
15091
+ serverHandler: false,
15092
+ customClientEntry: true,
15093
+ entries: {
15094
+ rsc: "virtual:timber-rsc-entry",
15095
+ ssr: "virtual:timber-ssr-entry",
15096
+ client: "virtual:timber-browser-entry"
15097
+ },
15098
+ clientChunks: (meta) => clientChunkGroup(meta, ctx.appDir)
15099
+ };
15100
+ Object.defineProperty(options, "enableActionEncryption", {
15101
+ get() {
15102
+ return shouldEnableEncryption(ctx.dev, ctx.config.actionEncryption);
15103
+ },
15104
+ enumerable: true
15105
+ });
15106
+ if (encryptionKeyExpr) options.defineEncryptionKey = encryptionKeyExpr;
15107
+ return options;
15108
+ }
15013
15109
  function timberCache(_ctx) {
15014
15110
  return cacheTransformPlugin();
15015
15111
  }
15112
+ /**
15113
+ * Create the timber Vite plugin array.
15114
+ *
15115
+ * Loads timber.config.ts and all dependencies synchronously before
15116
+ * constructing the plugin array. This ensures ALL plugins — including
15117
+ * the RSC plugin and React Compiler — see the fully merged config
15118
+ * (inline + file-based). No async, no deferred config, no stale reads.
15119
+ *
15120
+ * Requires Node >= 22.12 for synchronous require() of ESM modules
15121
+ * (@vitejs/plugin-rsc is ESM-only).
15122
+ *
15123
+ * Previous versions used async loading and deferred config merging,
15124
+ * causing file-based config for reactCompiler, actionEncryption, and
15125
+ * output mode to be silently ignored. See TIM-451.
15126
+ */
15016
15127
  function timber(config) {
15017
15128
  const ctx = createPluginContext(config);
15129
+ const consumerRequire = createRequire(join(process.cwd(), "package.json"));
15130
+ ctx.timer.start("rsc-plugin-import");
15131
+ const rscMod = consumerRequire("@vitejs/plugin-rsc");
15132
+ const vitePluginRsc = rscMod.default ?? rscMod;
15133
+ ctx.timer.end("rsc-plugin-import");
15134
+ const encryptionKeyExpr = resolveEncryptionKeyExpression();
15018
15135
  const rootSync = {
15019
15136
  name: "timber-root-sync",
15020
- async config(userConfig, { command }) {
15021
- const root = userConfig.root ?? process.cwd();
15137
+ config(userConfig, { command }) {
15138
+ const viteRoot = resolve(userConfig.root ?? process.cwd());
15022
15139
  ctx.timer.start("config-load");
15023
- const fileConfig = await loadTimberConfigFile(root);
15140
+ const fileConfig = loadTimberConfigFile(viteRoot);
15024
15141
  if (fileConfig) {
15025
15142
  mergeFileConfig(ctx, fileConfig);
15026
15143
  ctx.clientJavascript = resolveClientJavascript(ctx.config);
15027
15144
  }
15145
+ ctx.config.output ??= "server";
15028
15146
  ctx.timer.end("config-load");
15029
15147
  if (command === "build") return { oxc: { jsx: { development: false } } };
15030
15148
  },
@@ -15036,34 +15154,31 @@ function timber(config) {
15036
15154
  else ctx.timer.start("dev-server-setup");
15037
15155
  }
15038
15156
  };
15039
- const rscPluginPath = createRequire(join(process.cwd(), "package.json")).resolve("@vitejs/plugin-rsc");
15040
- ctx.timer.start("rsc-plugin-import");
15041
- const rscPluginsPromise = import(pathToFileURL(rscPluginPath).href).then(({ default: vitePluginRsc }) => {
15042
- ctx.timer.end("rsc-plugin-import");
15043
- return vitePluginRsc({
15044
- serverHandler: false,
15045
- customClientEntry: true,
15046
- entries: {
15047
- rsc: "virtual:timber-rsc-entry",
15048
- ssr: "virtual:timber-ssr-entry",
15049
- client: "virtual:timber-browser-entry"
15050
- },
15051
- clientChunks: () => "client-refs"
15052
- });
15053
- });
15157
+ const reactCompilerPlugins = [];
15158
+ if (config?.reactCompiler) reactCompilerPlugins.push(resolveReactCompilerPlugin(config.reactCompiler, consumerRequire));
15159
+ const lazyReactCompiler = {
15160
+ name: "timber-react-compiler",
15161
+ configResolved() {
15162
+ if (config?.reactCompiler || !ctx.config.reactCompiler) return;
15163
+ const resolved = resolveReactCompilerPlugin(ctx.config.reactCompiler, consumerRequire);
15164
+ for (const key of Object.keys(resolved)) if (key !== "name") lazyReactCompiler[key] = resolved[key];
15165
+ }
15166
+ };
15167
+ // @vitejs/plugin-rsc handles:
15054
15168
  return [
15055
15169
  rootSync,
15056
15170
  timberReactProd(),
15057
15171
  react(),
15172
+ ...reactCompilerPlugins,
15173
+ lazyReactCompiler,
15058
15174
  timberServerActionExports(),
15059
- rscPluginsPromise,
15175
+ vitePluginRsc(createRscOptions(ctx, encryptionKeyExpr)),
15060
15176
  timberShims(ctx),
15061
15177
  timberRouting(ctx),
15062
15178
  timberEntries(ctx),
15063
15179
  timberBuildManifest(ctx),
15064
15180
  timberCache(ctx),
15065
15181
  timberStaticBuild(ctx),
15066
- timberDynamicTransform(ctx),
15067
15182
  timberFonts(ctx),
15068
15183
  timberMdx(ctx),
15069
15184
  timberContent(ctx),
@@ -15075,7 +15190,28 @@ function timber(config) {
15075
15190
  timberDevServer(ctx)
15076
15191
  ];
15077
15192
  }
15193
+ /**
15194
+ * Type-safe helper for timber.config.ts files.
15195
+ *
15196
+ * A pass-through identity function that provides autocomplete and
15197
+ * type checking for timber configuration. No runtime validation —
15198
+ * purely a DX convenience (same pattern as Vite's defineConfig).
15199
+ *
15200
+ * @example
15201
+ * ```ts
15202
+ * // timber.config.ts
15203
+ * import { defineConfig } from '@timber-js/app';
15204
+ *
15205
+ * export default defineConfig({
15206
+ * output: 'server',
15207
+ * pageExtensions: ['tsx', 'ts', 'mdx'],
15208
+ * });
15209
+ * ```
15210
+ */
15211
+ function defineConfig(config) {
15212
+ return config;
15213
+ }
15078
15214
  //#endregion
15079
- export { timber as default, timber, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
15215
+ export { timber as default, timber, defineConfig, loadTimberConfigFile, resolveAppDir, resolveClientJavascript, warnConfigConflicts };
15080
15216
 
15081
15217
  //# sourceMappingURL=index.js.map