@timber-js/app 0.2.0-alpha.71 → 0.2.0-alpha.73

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 (248) hide show
  1. package/dist/_chunks/actions-Dg-ANYHb.js +421 -0
  2. package/dist/_chunks/actions-Dg-ANYHb.js.map +1 -0
  3. package/dist/_chunks/{als-registry-BJARkOcu.js → als-registry-HS0LGUl2.js} +1 -1
  4. package/dist/_chunks/als-registry-HS0LGUl2.js.map +1 -0
  5. package/dist/_chunks/{define-Dz1bqwaS.js → define-C77ScO0m.js} +14 -14
  6. package/dist/_chunks/define-C77ScO0m.js.map +1 -0
  7. package/dist/_chunks/{define-CGuYoRHU.js → define-CZqDwhSu.js} +15 -15
  8. package/dist/_chunks/define-CZqDwhSu.js.map +1 -0
  9. package/dist/_chunks/{define-cookie-B5mewxwM.js → define-cookie-C2IkoFGN.js} +9 -8
  10. package/dist/_chunks/{define-cookie-B5mewxwM.js.map → define-cookie-C2IkoFGN.js.map} +1 -1
  11. package/dist/_chunks/{format-Rn922VH2.js → dev-warnings-DpGRGoDi.js} +4 -26
  12. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +1 -0
  13. package/dist/_chunks/format-CYBGxKtc.js +14 -0
  14. package/dist/_chunks/format-CYBGxKtc.js.map +1 -0
  15. package/dist/_chunks/{interception-CEdHHviP.js → interception-Dpn_UfAD.js} +2 -2
  16. package/dist/_chunks/{interception-CEdHHviP.js.map → interception-Dpn_UfAD.js.map} +1 -1
  17. package/dist/_chunks/{segment-context-hzuJ048X.js → merge-search-params-Cm_KIWDX.js} +2 -33
  18. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
  19. package/dist/_chunks/{request-context-CywiO4jV.js → request-context-qMsWgy9C.js} +72 -36
  20. package/dist/_chunks/request-context-qMsWgy9C.js.map +1 -0
  21. package/dist/_chunks/{schema-bridge-C4SwjCQD.js → schema-bridge-C3xl_vfb.js} +1 -1
  22. package/dist/_chunks/{schema-bridge-C4SwjCQD.js.map → schema-bridge-C3xl_vfb.js.map} +1 -1
  23. package/dist/_chunks/segment-context-fHFLF1PE.js +34 -0
  24. package/dist/_chunks/segment-context-fHFLF1PE.js.map +1 -0
  25. package/dist/_chunks/ssr-data-DzuI0bIV.js +88 -0
  26. package/dist/_chunks/ssr-data-DzuI0bIV.js.map +1 -0
  27. package/dist/_chunks/{stale-reload-BLUC_Pl_.js → stale-reload-C2plcNtG.js} +1 -1
  28. package/dist/_chunks/{stale-reload-BLUC_Pl_.js.map → stale-reload-C2plcNtG.js.map} +1 -1
  29. package/dist/_chunks/{handler-store-BVePM7hp.js → tracing-CCYbKn5n.js} +60 -60
  30. package/dist/_chunks/tracing-CCYbKn5n.js.map +1 -0
  31. package/dist/_chunks/use-params-B1AuhI1p.js +307 -0
  32. package/dist/_chunks/use-params-B1AuhI1p.js.map +1 -0
  33. package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-Lo_s_pw2.js} +4 -4
  34. package/dist/_chunks/use-query-states-Lo_s_pw2.js.map +1 -0
  35. package/dist/_chunks/{wrappers-LZbghvn0.js → wrappers-_DTmImGt.js} +1 -1
  36. package/dist/_chunks/{wrappers-LZbghvn0.js.map → wrappers-_DTmImGt.js.map} +1 -1
  37. package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
  38. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
  39. package/dist/adapters/cloudflare-kv-cache.js +95 -0
  40. package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
  41. package/dist/cache/index.d.ts +18 -4
  42. package/dist/cache/index.d.ts.map +1 -1
  43. package/dist/cache/index.js +78 -12
  44. package/dist/cache/index.js.map +1 -1
  45. package/dist/cache/sizeof.d.ts +22 -0
  46. package/dist/cache/sizeof.d.ts.map +1 -0
  47. package/dist/cli.d.ts +6 -1
  48. package/dist/cli.d.ts.map +1 -1
  49. package/dist/cli.js +6 -1
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client/browser-dev.d.ts +27 -1
  52. package/dist/client/browser-dev.d.ts.map +1 -1
  53. package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
  54. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
  55. package/dist/client/browser-entry/hmr.d.ts +21 -0
  56. package/dist/client/browser-entry/hmr.d.ts.map +1 -0
  57. package/dist/client/browser-entry/hydrate.d.ts +46 -0
  58. package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
  59. package/dist/client/browser-entry/index.d.ts +30 -0
  60. package/dist/client/browser-entry/index.d.ts.map +1 -0
  61. package/dist/client/browser-entry/post-hydration.d.ts +26 -0
  62. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
  63. package/dist/client/browser-entry/router-init.d.ts +23 -0
  64. package/dist/client/browser-entry/router-init.d.ts.map +1 -0
  65. package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
  66. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
  67. package/dist/client/browser-entry/scroll.d.ts +19 -0
  68. package/dist/client/browser-entry/scroll.d.ts.map +1 -0
  69. package/dist/client/error-boundary.js +131 -1
  70. package/dist/client/error-boundary.js.map +1 -0
  71. package/dist/client/index.d.ts +4 -19
  72. package/dist/client/index.d.ts.map +1 -1
  73. package/dist/client/index.js +14 -1191
  74. package/dist/client/index.js.map +1 -1
  75. package/dist/client/internal.d.ts +18 -0
  76. package/dist/client/internal.d.ts.map +1 -0
  77. package/dist/client/internal.js +890 -0
  78. package/dist/client/internal.js.map +1 -0
  79. package/dist/client/navigation-context.d.ts.map +1 -1
  80. package/dist/client/router-ref.d.ts +1 -1
  81. package/dist/client/top-loader.d.ts +2 -2
  82. package/dist/client/use-link-status.d.ts +1 -1
  83. package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
  84. package/dist/client/use-pending-navigation.d.ts.map +1 -0
  85. package/dist/client/use-router.d.ts +1 -1
  86. package/dist/codec.d.ts +10 -0
  87. package/dist/codec.d.ts.map +1 -1
  88. package/dist/codec.js +1 -1
  89. package/dist/config-types.d.ts +210 -0
  90. package/dist/config-types.d.ts.map +1 -0
  91. package/dist/content/index.d.ts +1 -10
  92. package/dist/content/index.d.ts.map +1 -1
  93. package/dist/content/index.js +0 -2
  94. package/dist/cookies/define-cookie.d.ts.map +1 -1
  95. package/dist/cookies/index.d.ts +0 -2
  96. package/dist/cookies/index.d.ts.map +1 -1
  97. package/dist/cookies/index.js +2 -3
  98. package/dist/index.d.ts +25 -288
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +261 -43
  101. package/dist/index.js.map +1 -1
  102. package/dist/plugin-context.d.ts +84 -0
  103. package/dist/plugin-context.d.ts.map +1 -0
  104. package/dist/plugins/adapter-build.d.ts +1 -1
  105. package/dist/plugins/adapter-build.d.ts.map +1 -1
  106. package/dist/plugins/build-manifest.d.ts +1 -1
  107. package/dist/plugins/build-manifest.d.ts.map +1 -1
  108. package/dist/plugins/build-report.d.ts +1 -1
  109. package/dist/plugins/build-report.d.ts.map +1 -1
  110. package/dist/plugins/content.d.ts +1 -1
  111. package/dist/plugins/content.d.ts.map +1 -1
  112. package/dist/plugins/dev-browser-logs.d.ts +1 -1
  113. package/dist/plugins/dev-browser-logs.d.ts.map +1 -1
  114. package/dist/plugins/dev-logs.d.ts +1 -1
  115. package/dist/plugins/dev-logs.d.ts.map +1 -1
  116. package/dist/plugins/dev-server.d.ts +1 -1
  117. package/dist/plugins/dev-server.d.ts.map +1 -1
  118. package/dist/plugins/entries.d.ts +1 -1
  119. package/dist/plugins/entries.d.ts.map +1 -1
  120. package/dist/plugins/fonts.d.ts +1 -1
  121. package/dist/plugins/fonts.d.ts.map +1 -1
  122. package/dist/plugins/mdx.d.ts +1 -1
  123. package/dist/plugins/mdx.d.ts.map +1 -1
  124. package/dist/plugins/routing.d.ts +1 -1
  125. package/dist/plugins/routing.d.ts.map +1 -1
  126. package/dist/plugins/shims.d.ts +1 -1
  127. package/dist/plugins/shims.d.ts.map +1 -1
  128. package/dist/plugins/static-build.d.ts +4 -4
  129. package/dist/plugins/static-build.d.ts.map +1 -1
  130. package/dist/routing/index.js +1 -1
  131. package/dist/search-params/define.d.ts +6 -6
  132. package/dist/search-params/define.d.ts.map +1 -1
  133. package/dist/search-params/index.d.ts +1 -2
  134. package/dist/search-params/index.d.ts.map +1 -1
  135. package/dist/search-params/index.js +4 -4
  136. package/dist/search-params/registry.d.ts +1 -1
  137. package/dist/search-params/registry.d.ts.map +1 -1
  138. package/dist/segment-params/define.d.ts +6 -6
  139. package/dist/segment-params/define.d.ts.map +1 -1
  140. package/dist/segment-params/index.d.ts +0 -1
  141. package/dist/segment-params/index.d.ts.map +1 -1
  142. package/dist/segment-params/index.js +3 -3
  143. package/dist/server/als-registry.d.ts +1 -1
  144. package/dist/server/dev-holding-server.d.ts +52 -0
  145. package/dist/server/dev-holding-server.d.ts.map +1 -0
  146. package/dist/server/dev-warnings.d.ts +1 -7
  147. package/dist/server/dev-warnings.d.ts.map +1 -1
  148. package/dist/server/index.d.ts +6 -45
  149. package/dist/server/index.d.ts.map +1 -1
  150. package/dist/server/index.js +7 -3272
  151. package/dist/server/index.js.map +1 -1
  152. package/dist/server/internal.d.ts +46 -0
  153. package/dist/server/internal.d.ts.map +1 -0
  154. package/dist/server/internal.js +2865 -0
  155. package/dist/server/internal.js.map +1 -0
  156. package/dist/server/pipeline.d.ts.map +1 -1
  157. package/dist/server/primitives.d.ts +41 -17
  158. package/dist/server/primitives.d.ts.map +1 -1
  159. package/dist/server/request-context.d.ts +45 -15
  160. package/dist/server/request-context.d.ts.map +1 -1
  161. package/dist/server/tracing.d.ts +4 -4
  162. package/dist/server/tracing.d.ts.map +1 -1
  163. package/dist/shims/headers.d.ts +2 -1
  164. package/dist/shims/headers.d.ts.map +1 -1
  165. package/dist/shims/navigation.d.ts +2 -1
  166. package/dist/shims/navigation.d.ts.map +1 -1
  167. package/package.json +19 -13
  168. package/src/adapters/cloudflare-kv-cache.ts +142 -0
  169. package/src/cache/handler-store.ts +2 -2
  170. package/src/cache/index.ts +74 -15
  171. package/src/cache/sizeof.ts +31 -0
  172. package/src/cli.ts +6 -1
  173. package/src/client/browser-dev.ts +128 -1
  174. package/src/client/browser-entry/action-dispatch.ts +116 -0
  175. package/src/client/browser-entry/hmr.ts +81 -0
  176. package/src/client/browser-entry/hydrate.ts +145 -0
  177. package/src/client/browser-entry/index.ts +138 -0
  178. package/src/client/browser-entry/post-hydration.ts +119 -0
  179. package/src/client/browser-entry/router-init.ts +184 -0
  180. package/src/client/browser-entry/rsc-stream.ts +157 -0
  181. package/src/client/browser-entry/scroll.ts +27 -0
  182. package/src/client/index.ts +10 -38
  183. package/src/client/internal.ts +57 -0
  184. package/src/client/navigation-context.ts +6 -2
  185. package/src/client/navigation-root.tsx +1 -1
  186. package/src/client/router-ref.ts +1 -1
  187. package/src/client/top-loader.tsx +2 -2
  188. package/src/client/use-link-status.ts +1 -1
  189. package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +5 -5
  190. package/src/client/use-query-states.ts +2 -2
  191. package/src/client/use-router.ts +1 -1
  192. package/src/codec.ts +15 -0
  193. package/src/config-types.ts +208 -0
  194. package/src/content/index.ts +5 -13
  195. package/src/cookies/define-cookie.ts +9 -7
  196. package/src/cookies/index.ts +6 -5
  197. package/src/index.ts +84 -416
  198. package/src/plugin-context.ts +200 -0
  199. package/src/plugins/adapter-build.ts +1 -1
  200. package/src/plugins/build-manifest.ts +1 -1
  201. package/src/plugins/build-report.ts +1 -1
  202. package/src/plugins/content.ts +1 -1
  203. package/src/plugins/dev-browser-logs.ts +1 -1
  204. package/src/plugins/dev-logs.ts +1 -1
  205. package/src/plugins/dev-server.ts +16 -1
  206. package/src/plugins/entries.ts +2 -2
  207. package/src/plugins/fonts.ts +4 -3
  208. package/src/plugins/mdx.ts +1 -1
  209. package/src/plugins/routing.ts +1 -1
  210. package/src/plugins/shims.ts +53 -5
  211. package/src/plugins/static-build.ts +8 -6
  212. package/src/search-params/define.ts +22 -22
  213. package/src/search-params/index.ts +3 -3
  214. package/src/search-params/registry.ts +1 -1
  215. package/src/segment-params/define.ts +18 -18
  216. package/src/segment-params/index.ts +2 -1
  217. package/src/server/action-handler.ts +1 -1
  218. package/src/server/als-registry.ts +3 -3
  219. package/src/server/dev-holding-server.ts +185 -0
  220. package/src/server/dev-warnings.ts +2 -21
  221. package/src/server/html-injectors.ts +3 -3
  222. package/src/server/index.ts +25 -180
  223. package/src/server/internal.ts +169 -0
  224. package/src/server/pipeline.ts +12 -7
  225. package/src/server/primitives.ts +71 -30
  226. package/src/server/request-context.ts +77 -39
  227. package/src/server/route-element-builder.ts +1 -1
  228. package/src/server/rsc-entry/index.ts +2 -2
  229. package/src/server/rsc-entry/ssr-renderer.ts +1 -1
  230. package/src/server/slot-resolver.ts +1 -1
  231. package/src/server/tracing.ts +6 -6
  232. package/src/server/tree-builder.ts +1 -1
  233. package/src/shims/headers.ts +5 -1
  234. package/src/shims/navigation.ts +5 -1
  235. package/dist/_chunks/als-registry-BJARkOcu.js.map +0 -1
  236. package/dist/_chunks/define-CGuYoRHU.js.map +0 -1
  237. package/dist/_chunks/define-Dz1bqwaS.js.map +0 -1
  238. package/dist/_chunks/error-boundary-D9hzsveV.js +0 -216
  239. package/dist/_chunks/error-boundary-D9hzsveV.js.map +0 -1
  240. package/dist/_chunks/format-Rn922VH2.js.map +0 -1
  241. package/dist/_chunks/handler-store-BVePM7hp.js.map +0 -1
  242. package/dist/_chunks/request-context-CywiO4jV.js.map +0 -1
  243. package/dist/_chunks/segment-context-hzuJ048X.js.map +0 -1
  244. package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +0 -1
  245. package/dist/client/browser-entry.d.ts +0 -21
  246. package/dist/client/browser-entry.d.ts.map +0 -1
  247. package/dist/client/use-navigation-pending.d.ts.map +0 -1
  248. package/src/client/browser-entry.ts +0 -846
@@ -22,30 +22,30 @@ import {
22
22
  } from '../schema-bridge.js';
23
23
 
24
24
  // ---------------------------------------------------------------------------
25
- // Server-only ALS reference for .load()
25
+ // Server-only ALS reference for .get()
26
26
  // ---------------------------------------------------------------------------
27
27
 
28
28
  // Same pattern as search-params: eagerly registered at server startup
29
29
  // to avoid dynamic imports that lose ALS context. See TIM-523.
30
- let _rawSegmentParams: (() => Promise<Record<string, string | string[]>>) | undefined;
30
+ let _getSegmentParamsFn: (() => Promise<Record<string, string | string[]>>) | undefined;
31
31
 
32
32
  /**
33
- * Register the rawSegmentParams function. Called once at module load time
33
+ * Register the getSegmentParams function. Called once at module load time
34
34
  * from request-context.ts to avoid dynamic import at call time.
35
35
  * @internal
36
36
  */
37
- export function _setRawSegmentParamsFn(fn: () => Promise<Record<string, string | string[]>>): void {
38
- _rawSegmentParams = fn;
37
+ export function _setGetSegmentParamsFn(fn: () => Promise<Record<string, string | string[]>>): void {
38
+ _getSegmentParamsFn = fn;
39
39
  }
40
40
 
41
- function getRawSegmentParams(): Promise<Record<string, string | string[]>> {
42
- if (!_rawSegmentParams) {
41
+ function getSegmentParamsFromAls(): Promise<Record<string, string | string[]>> {
42
+ if (!_getSegmentParamsFn) {
43
43
  throw new Error(
44
- '[timber] segmentParams.load() is only available on the server. ' +
44
+ '[timber] segmentParams.get() is only available on the server. ' +
45
45
  'Use useSegmentParams() on the client.'
46
46
  );
47
47
  }
48
- return _rawSegmentParams();
48
+ return _getSegmentParamsFn();
49
49
  }
50
50
 
51
51
  // ---------------------------------------------------------------------------
@@ -75,9 +75,9 @@ export interface ParamsDefinition<T extends Record<string, unknown>> {
75
75
  serialize(values: T): Record<string, string>;
76
76
 
77
77
  /**
78
- * Load typed segment params from the current request context (ALS).
78
+ * Get typed segment params from the current request context (ALS).
79
79
  *
80
- * Server-only. Reads rawSegmentParams() from ALS and coerces through
80
+ * Server-only. Reads getSegmentParams() from ALS and coerces through
81
81
  * this definition's codecs, returning fully typed params.
82
82
  *
83
83
  * ```ts
@@ -87,11 +87,11 @@ export interface ParamsDefinition<T extends Record<string, unknown>> {
87
87
  * // app/products/[id]/page.tsx
88
88
  * import { segmentParams } from './params'
89
89
  * export default async function Page() {
90
- * const { id } = await segmentParams.load() // id: number
90
+ * const { id } = await segmentParams.get() // id: number
91
91
  * }
92
92
  * ```
93
93
  */
94
- load(): Promise<T>;
94
+ get(): Promise<T>;
95
95
 
96
96
  /** Read-only codec map. */
97
97
  codecs: { [K in keyof T]: Codec<T[K]> };
@@ -248,7 +248,7 @@ export function defineSegmentParams<C extends Record<string, ParamField>>(
248
248
  return result;
249
249
  }
250
250
 
251
- // ---- load ----
251
+ // ---- get ----
252
252
  // ALS-backed: reads segment params from the current request context.
253
253
  // Server-only — throws on client.
254
254
  //
@@ -257,13 +257,13 @@ export function defineSegmentParams<C extends Record<string, ParamField>>(
257
257
  // We return those directly instead of re-parsing, because codecs may
258
258
  // not be idempotent (e.g., a codec that only accepts raw strings would
259
259
  // throw if given an already-parsed value). See TIM-574.
260
- async function load(): Promise<T> {
260
+ async function get(): Promise<T> {
261
261
  if (typeof window !== 'undefined') {
262
262
  throw new Error(
263
- '[timber] segmentParams.load() is server-only. ' + 'Use useSegmentParams() on the client.'
263
+ '[timber] segmentParams.get() is server-only. ' + 'Use useSegmentParams() on the client.'
264
264
  );
265
265
  }
266
- const params = await getRawSegmentParams();
266
+ const params = await getSegmentParamsFromAls();
267
267
  // params are already coerced by the pipeline — return as-is.
268
268
  return params as unknown as T;
269
269
  }
@@ -271,7 +271,7 @@ export function defineSegmentParams<C extends Record<string, ParamField>>(
271
271
  const definition: ParamsDefinition<T> = {
272
272
  parse,
273
273
  serialize,
274
- load,
274
+ get,
275
275
  codecs: resolvedCodecs as { [K in keyof T]: Codec<T[K]> },
276
276
  };
277
277
 
@@ -17,7 +17,8 @@ export { defineSearchParams } from '../search-params/define.js';
17
17
  // Codec bridges moved to @timber-js/app/codec
18
18
  // Import fromSchema / fromArraySchema from '@timber-js/app/codec' instead.
19
19
  export { withDefault, withUrlKey } from '../search-params/wrappers.js';
20
- export type { Codec } from '../codec.js';
20
+ // Codec is the canonical home for the Codec type — import from
21
+ // @timber-js/app/codec. Re-export removed per TIM-721.
21
22
  export type {
22
23
  SearchParamCodec,
23
24
  SearchParamsDefinition,
@@ -123,7 +123,7 @@ export async function handleActionRequest(
123
123
  return new Response(null, { status: limitsResult.status });
124
124
  }
125
125
 
126
- // Run inside request context so headers(), cookies() work in actions.
126
+ // Run inside request context so getHeaders(), getCookies() work in actions.
127
127
  // Actions are a mutable context — they can set cookies (design/29-cookies.md).
128
128
  return runWithRequestContext(req, async () => {
129
129
  setMutableCookieContext(true);
@@ -24,7 +24,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
24
24
  import type { DebugComponentEntry } from './rsc-entry/helpers.js';
25
25
 
26
26
  // ─── Request Context ──────────────────────────────────────────────────────
27
- // Used by: request-context.ts (headers(), cookies(), searchParams())
27
+ // Used by: request-context.ts (getHeaders(), getCookies(), getSearchParams())
28
28
  // Design doc: design/04-authorization.md
29
29
 
30
30
  /** @internal — import via request-context.ts public API */
@@ -54,7 +54,7 @@ export interface RequestContextStore {
54
54
  * Promise resolving to the coerced segment params for the current request.
55
55
  * Set by the pipeline after route matching and param coercion, before
56
56
  * middleware and rendering. Pages and layouts read params via
57
- * `rawSegmentParams()` instead of receiving them as a prop.
57
+ * `getSegmentParams()` instead of receiving them as a prop.
58
58
  *
59
59
  * See design/07-routing.md §"params.ts — Convention File for Typed Params"
60
60
  */
@@ -88,7 +88,7 @@ export interface CookieEntry {
88
88
  }
89
89
 
90
90
  // ─── Tracing ──────────────────────────────────────────────────────────────
91
- // Used by: tracing.ts (traceId(), spanId())
91
+ // Used by: tracing.ts (getTraceId(), getSpanId())
92
92
  // Design doc: design/17-logging.md
93
93
 
94
94
  export interface TraceStore {
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Dev holding server — serves a loading page while Vite initializes.
3
+ *
4
+ * When `timber dev` (or `vite dev`) starts, Vite takes several seconds
5
+ * to initialize all plugins, scan routes, and boot the RSC environment.
6
+ * During this window, the port is unbound and browsers get
7
+ * ERR_CONNECTION_REFUSED.
8
+ *
9
+ * This module creates a lightweight HTTP server that binds the port
10
+ * immediately and serves a branded holding page with auto-refresh.
11
+ * Once Vite is ready, the holding server is closed and Vite's server
12
+ * takes over the port.
13
+ *
14
+ * The holding server is started in rootSync's config() hook (the
15
+ * earliest Vite plugin lifecycle point where we know the port) and
16
+ * closed in timber-dev-server's configureServer() hook (the last
17
+ * plugin, right before Vite calls server.listen()).
18
+ *
19
+ * Browser requests (Accept: text/html) get the HTML holding page.
20
+ * API requests (Accept: application/json) get a 503 JSON response.
21
+ * All responses include Retry-After: 1.
22
+ *
23
+ * See design/21-dev-server.md, TIM-665.
24
+ */
25
+
26
+ import http from 'node:http';
27
+
28
+ // ─── Types ────────────────────────────────────────────────────────────────
29
+
30
+ export interface HoldingServer {
31
+ /**
32
+ * Start listening on the given port.
33
+ * Returns the actual bound port (useful when port is 0 for OS assignment).
34
+ */
35
+ listen(port: number): Promise<number>;
36
+
37
+ /** Gracefully close the server and stop accepting connections. */
38
+ close(): Promise<void>;
39
+ }
40
+
41
+ // ─── Holding Page HTML ────────────────────────────────────────────────────
42
+
43
+ /**
44
+ * Minimal self-contained HTML holding page.
45
+ *
46
+ * Matches the timber.js docs site theme:
47
+ * - Light mode: warm stone/grain background, timber/bark text
48
+ * - Dark mode: stone-900 background, stone-200 text
49
+ * - 🪵 timber.js branding
50
+ *
51
+ * Uses meta-refresh (1s) for auto-reload — works without JavaScript.
52
+ * Inline CSS only, no external dependencies.
53
+ */
54
+ const HOLDING_PAGE_HTML = [
55
+ '<!DOCTYPE html>',
56
+ '<html lang="en">',
57
+ '<head>',
58
+ ' <meta charset="utf-8">',
59
+ ' <meta name="viewport" content="width=device-width, initial-scale=1">',
60
+ ' <meta http-equiv="refresh" content="2">',
61
+ ' <title>timber — starting...</title>',
62
+ ' <style>',
63
+ ' *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }',
64
+ ' body {',
65
+ ' font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;',
66
+ ' background: #fafaf9;',
67
+ ' color: #44403c;',
68
+ ' display: flex;',
69
+ ' align-items: center;',
70
+ ' justify-content: center;',
71
+ ' min-height: 100vh;',
72
+ ' -webkit-font-smoothing: antialiased;',
73
+ ' }',
74
+ ' @media (prefers-color-scheme: dark) {',
75
+ ' body { background: #1c1917; color: #e7e5e4; }',
76
+ ' .message { color: #a8a29e; }',
77
+ ' .spinner { border-color: #57534e; border-top-color: #a1887f; }',
78
+ ' .logo { color: #e7e5e4; }',
79
+ ' }',
80
+ ' .container { text-align: center; padding: 2rem; }',
81
+ ' .logo {',
82
+ ' font-size: 1.5rem;',
83
+ ' font-weight: 700;',
84
+ ' letter-spacing: -0.02em;',
85
+ ' color: #5c3d2e;',
86
+ ' margin-bottom: 1.5rem;',
87
+ ' }',
88
+ ' .message {',
89
+ ' font-size: 0.95rem;',
90
+ ' color: #78716c;',
91
+ ' margin-bottom: 2rem;',
92
+ ' }',
93
+ ' .spinner {',
94
+ ' width: 24px;',
95
+ ' height: 24px;',
96
+ ' border: 2.5px solid #d7ccc8;',
97
+ ' border-top-color: #6d4c41;',
98
+ ' border-radius: 50%;',
99
+ ' animation: spin 0.8s linear infinite;',
100
+ ' margin: 0 auto;',
101
+ ' }',
102
+ ' @keyframes spin { to { transform: rotate(360deg); } }',
103
+ ' </style>',
104
+ '</head>',
105
+ '<body>',
106
+ ' <div class="container">',
107
+ ' <div class="logo">\u{1FAB5} timber.js</div>',
108
+ ' <p class="message">Dev server starting&hellip;</p>',
109
+ ' <div class="spinner"></div>',
110
+ ' </div>',
111
+ '</body>',
112
+ '</html>',
113
+ ].join('\n');
114
+
115
+ // ─── Factory ──────────────────────────────────────────────────────────────
116
+
117
+ /**
118
+ * Create a holding HTTP server that serves a loading page during startup.
119
+ *
120
+ * Usage (inside Vite plugin):
121
+ * ```ts
122
+ * // In config() hook — earliest point where port is known
123
+ * const holding = createHoldingServer();
124
+ * holding.listen(config.server?.port ?? 5173);
125
+ *
126
+ * // In last plugin's configureServer() — wrap listen for seamless handoff
127
+ * const originalListen = server.listen.bind(server);
128
+ * server.listen = async (...args) => {
129
+ * await holding.close();
130
+ * return originalListen(...args);
131
+ * };
132
+ * ```
133
+ */
134
+ export function createHoldingServer(): HoldingServer {
135
+ const server = http.createServer((req, res) => {
136
+ const accept = req.headers.accept ?? '';
137
+
138
+ // API requests: JSON 503
139
+ if (accept.includes('application/json') && !accept.includes('text/html')) {
140
+ res.writeHead(503, {
141
+ 'Content-Type': 'application/json; charset=utf-8',
142
+ 'Retry-After': '1',
143
+ 'Cache-Control': 'no-store',
144
+ });
145
+ res.end(
146
+ JSON.stringify({
147
+ error: 'Service Unavailable',
148
+ message: 'timber dev server is starting — please retry shortly',
149
+ })
150
+ );
151
+ return;
152
+ }
153
+
154
+ // Browser and other requests: HTML holding page
155
+ res.writeHead(503, {
156
+ 'Content-Type': 'text/html; charset=utf-8',
157
+ 'Retry-After': '1',
158
+ 'Cache-Control': 'no-store',
159
+ });
160
+ res.end(HOLDING_PAGE_HTML);
161
+ });
162
+
163
+ return {
164
+ listen(port: number): Promise<number> {
165
+ return new Promise((resolve, reject) => {
166
+ server.once('error', reject);
167
+ server.listen(port, () => {
168
+ server.removeListener('error', reject);
169
+ const addr = server.address();
170
+ const boundPort = typeof addr === 'object' && addr ? addr.port : port;
171
+ resolve(boundPort);
172
+ });
173
+ });
174
+ },
175
+
176
+ close(): Promise<void> {
177
+ return new Promise((resolve, reject) => {
178
+ server.close((err) => {
179
+ if (err) reject(err);
180
+ else resolve();
181
+ });
182
+ });
183
+ },
184
+ };
185
+ }
@@ -110,7 +110,7 @@ export function warnSuspenseWrappingChildren(layoutFile: string): void {
110
110
  'warn',
111
111
  `Layout at ${layoutFile} wraps {children} in <Suspense>. ` +
112
112
  'This prevents child pages from setting HTTP status codes. ' +
113
- 'Use useNavigationPending() for loading states instead.'
113
+ 'Use usePendingNavigation() for loading states instead.'
114
114
  );
115
115
  }
116
116
 
@@ -178,7 +178,7 @@ export function warnRedirectInAccess(accessFile: string, line?: number): void {
178
178
  }
179
179
 
180
180
  /**
181
- * Warn when cookies() or headers() is called during a static build.
181
+ * Warn when getCookies() or getHeaders() is called during a static build.
182
182
  *
183
183
  * In output: 'static' mode, there is no per-request context — these APIs
184
184
  * read build-time values only. This is almost always a mistake.
@@ -219,25 +219,6 @@ export function warnSlowSlotWithoutSuspense(slotName: string, durationMs: number
219
219
  );
220
220
  }
221
221
 
222
- // ─── Legacy aliases ─────────────────────────────────────────────────────────
223
-
224
- /** @deprecated Use warnStaticRequestApi instead */
225
- export const warnDynamicApiInStaticBuild = warnStaticRequestApi;
226
-
227
- /** @deprecated Use warnRedirectInAccess instead */
228
- export function warnRedirectInSlotAccess(slotName: string): void {
229
- warnRedirectInAccess(`${slotName}/access.ts`);
230
- }
231
-
232
- /** @deprecated Use warnDenyInSuspense / warnRedirectInSuspense instead */
233
- export function warnDenyAfterFlush(signal: 'deny' | 'redirect'): void {
234
- if (signal === 'deny') {
235
- warnDenyInSuspense('unknown');
236
- } else {
237
- warnRedirectInSuspense('unknown');
238
- }
239
- }
240
-
241
222
  // ─── Testing ────────────────────────────────────────────────────────────────
242
223
 
243
224
  /**
@@ -504,7 +504,7 @@ export interface ClientBootstrapConfig {
504
504
  preloadLinks: string;
505
505
  }
506
506
 
507
- /** Find a manifest entry by matching the key suffix (e.g. 'client/browser-entry.ts'). */
507
+ /** Find a manifest entry by matching the key suffix (e.g. 'client/browser-entry/index.ts'). */
508
508
  function findManifestEntry(map: Record<string, string>, suffix: string): string | undefined {
509
509
  for (const [key, value] of Object.entries(map)) {
510
510
  if (key.endsWith(suffix)) return value;
@@ -585,7 +585,7 @@ export function buildClientScripts(runtimeConfig: {
585
585
  // a monorepo). Match by suffix to handle both cases.
586
586
  const manifest = runtimeConfig.buildManifest;
587
587
  const browserEntryUrl = manifest
588
- ? findManifestEntry(manifest.js, 'client/browser-entry.ts')
588
+ ? findManifestEntry(manifest.js, 'client/browser-entry/index.ts')
589
589
  : undefined;
590
590
 
591
591
  let preloadLinks = '';
@@ -595,7 +595,7 @@ export function buildClientScripts(runtimeConfig: {
595
595
  // Modulepreload hints for browser entry dependencies
596
596
  const preloads =
597
597
  (manifest
598
- ? findManifestEntryArray(manifest.modulepreload, 'client/browser-entry.ts')
598
+ ? findManifestEntryArray(manifest.modulepreload, 'client/browser-entry/index.ts')
599
599
  : undefined) ?? [];
600
600
  for (const url of preloads) {
601
601
  preloadLinks += `<link rel="modulepreload" href="${url}">`;
@@ -1,130 +1,45 @@
1
1
  // @timber-js/app/server — Server-side primitives
2
2
  // These are the primary imports for server components, middleware, and access files.
3
+ //
4
+ // Framework-internal pipeline plumbing is in #server-internal (Node package import).
5
+ // Design doc: design/triage/api-naming-spike.md §8.5
3
6
 
4
7
  export type { AccessContext } from './types';
5
8
  export type { MiddlewareContext } from './types';
6
9
  export type { RouteContext } from './types';
7
10
  export type { Metadata, MetadataRoute } from './types';
8
11
 
9
- // Request Context — ALS-backed headers(), cookies(), and rawSearchParams()
12
+ // Request Context — ALS-backed getHeaders(), getCookies(), and getSearchParams()
10
13
  // Design doc: design/04-authorization.md §"AccessContext does not include cookies or headers"
11
14
  // Design doc: design/23-search-params.md §"Server Integration"
12
15
  export {
13
- headers,
14
- cookies,
15
- rawSearchParams,
16
- rawSegmentParams,
17
- setSegmentParams,
18
- runWithRequestContext,
19
- setMutableCookieContext,
20
- markResponseFlushed,
21
- getSetCookieHeaders,
16
+ getHeaders,
17
+ getHeader,
18
+ getCookies,
19
+ getCookie,
20
+ getSearchParams,
21
+ getSegmentParams,
22
22
  } from './request-context';
23
23
  export type { ReadonlyHeaders, RequestCookies, CookieOptions } from './request-context';
24
24
 
25
25
  // Runtime primitives
26
26
  export {
27
27
  deny,
28
- notFound,
29
28
  redirect,
30
- permanentRedirect,
31
29
  redirectExternal,
32
30
  RedirectType,
33
- RenderError,
34
31
  waitUntil,
35
- DenySignal,
36
- RedirectSignal,
32
+ type DenyOptions,
37
33
  type RedirectOptions,
38
34
  } from './primitives';
35
+ // RenderError removed from public exports per TIM-724.
36
+ // Use deny({ status: 500, data: ... }) instead. RenderError stays in
37
+ // #server-internal for pipeline catch logic.
39
38
  export type { RenderErrorDigest, WaitUntilAdapter } from './primitives';
40
- export type { JsonSerializable } from './types';
39
+ // JsonSerializable moved to @timber-js/app/codec as the single canonical path.
40
+ // Re-export removed per TIM-721.
41
41
 
42
- // Pipeline
43
- export { createPipeline } from './pipeline';
44
- export type {
45
- PipelineConfig,
46
- RouteMatch,
47
- RouteMatcher,
48
- RouteRenderer,
49
- EarlyHintsEmitter,
50
- } from './pipeline';
51
-
52
- // Early Hints
53
- export { collectEarlyHintHeaders, formatLinkHeader } from './early-hints';
54
- export type { EarlyHint } from './early-hints';
55
-
56
- // Early Hints 103 Sender — ALS bridge for platform adapters
57
- export { runWithEarlyHintsSender, sendEarlyHints103 } from './early-hints-sender';
58
- export type { EarlyHintsSenderFn } from './early-hints-sender';
59
-
60
- // Canonicalization
61
- export { canonicalize } from './canonicalize';
62
- export type { CanonicalizeResult } from './canonicalize';
63
-
64
- // Proxy
65
- export { runProxy } from './proxy';
66
- export type { ProxyFn, ProxyExport } from './proxy';
67
-
68
- // Middleware
69
- export { runMiddleware } from './middleware-runner';
70
- export type { MiddlewareFn } from './middleware-runner';
71
-
72
- // Tree Builder
73
- export { buildElementTree } from './tree-builder';
74
- export type {
75
- TreeBuilderConfig,
76
- TreeBuildResult,
77
- LoadedModule,
78
- ModuleLoader,
79
- AccessGateProps,
80
- SlotAccessGateProps,
81
- ErrorBoundaryProps,
82
- } from './tree-builder';
83
-
84
- // Access Gates
85
- export { AccessGate, SlotAccessGate } from './access-gate';
86
-
87
- // Status-Code Resolver
88
- export { resolveStatusFile, resolveSlotDenied } from './status-code-resolver';
89
- export type {
90
- StatusFileResolution,
91
- StatusFileKind,
92
- StatusFileFormat,
93
- SlotDeniedResolution,
94
- SlotDeniedKind,
95
- } from './status-code-resolver';
96
-
97
- // Flush Controller
98
- export { flushResponse } from './flush';
99
- export type { FlushOptions, FlushResult, RenderFn, RenderResult } from './flush';
100
-
101
- // CSRF Protection
102
- export { validateCsrf } from './csrf';
103
- export type { CsrfConfig, CsrfResult } from './csrf';
104
-
105
- // Body Limits
106
- export { parseBodySize, enforceBodyLimits, DEFAULT_LIMITS } from './body-limits';
107
- export type { BodyLimitsConfig, BodyLimitResult, BodyKind } from './body-limits';
108
-
109
- // Metadata
110
- export {
111
- resolveMetadata,
112
- resolveTitle,
113
- resolveMetadataUrls,
114
- renderMetadataToElements,
115
- } from './metadata';
116
- export type { SegmentMetadataEntry, ResolveMetadataOptions, HeadElement } from './metadata';
117
-
118
- // Metadata Routes
119
- export {
120
- classifyMetadataRoute,
121
- getMetadataRouteServePath,
122
- getMetadataRouteAutoLink,
123
- METADATA_ROUTE_CONVENTIONS,
124
- } from './metadata-routes';
125
- export type { MetadataRouteInfo, MetadataRouteType } from './metadata-routes';
126
-
127
- // Server Actions
42
+ // Server Actions — user-facing action client and validation
128
43
  export { createActionClient, ActionError, validated } from './action-client';
129
44
  export type {
130
45
  ActionResult,
@@ -144,84 +59,14 @@ export { parseFormData, coerce } from './form-data';
144
59
  export { getFormFlash } from './form-flash';
145
60
  export type { FormFlashData } from './form-flash';
146
61
 
147
- // Revalidation
148
- export {
149
- revalidatePath,
150
- revalidateTag,
151
- executeAction,
152
- buildNoJsResponse,
153
- isRscActionRequest,
154
- } from './actions';
155
- export type {
156
- RevalidateRenderer,
157
- RevalidationState,
158
- ActionHandlerConfig,
159
- ActionHandlerResult,
160
- } from './actions';
62
+ // Revalidation — user-facing revalidation APIs
63
+ export { revalidatePath, revalidateTag } from './actions';
161
64
 
162
- // Tracing — per-request trace ID via ALS
65
+ // Tracing — per-request trace ID via ALS (user-facing accessors)
163
66
  // Design doc: design/17-logging.md §"trace_id is Always Set"
164
- export {
165
- traceId,
166
- spanId,
167
- generateTraceId,
168
- runWithTraceId,
169
- replaceTraceId,
170
- withSpan,
171
- addSpanEvent,
172
- } from './tracing';
173
- export type { TraceStore } from './tracing';
174
-
175
- // Logger — structured logging
176
- // Design doc: design/17-logging.md §"Production Logging"
177
- export { setLogger, getLogger } from './logger';
178
- export {
179
- logRequestCompleted,
180
- logRequestReceived,
181
- logSlowRequest,
182
- logMiddlewareShortCircuit,
183
- logMiddlewareError,
184
- logRenderError,
185
- logProxyError,
186
- logWaitUntilUnsupported,
187
- logWaitUntilRejected,
188
- logSwrRefetchFailed,
189
- logCacheMiss,
190
- } from './logger';
191
- export type { TimberLogger } from './logger';
192
-
193
- // Instrumentation — instrumentation.ts file convention
194
- // Design doc: design/17-logging.md §"instrumentation.ts"
195
- export { loadInstrumentation, callOnRequestError, hasOnRequestError } from './instrumentation';
196
- export type {
197
- InstrumentationOnRequestError,
198
- InstrumentationRequestInfo,
199
- InstrumentationErrorContext,
200
- } from './instrumentation';
201
-
202
- // Dev Warnings — dev-mode misuse detection
203
- // Design doc: design/21-dev-server.md §"Dev-Mode Warnings", design/11-platform.md §"Dev Mode"
204
- export {
205
- warnSuspenseWrappingChildren,
206
- warnDenyInSuspense,
207
- warnRedirectInSuspense,
208
- warnRedirectInAccess,
209
- warnStaticRequestApi,
210
- warnSlowSlotWithoutSuspense,
211
- setViteServer,
212
- WarningId,
213
- // Legacy aliases
214
- warnDynamicApiInStaticBuild,
215
- warnRedirectInSlotAccess,
216
- warnDenyAfterFlush,
217
- } from './dev-warnings';
218
- export type { DevWarningConfig } from './dev-warnings';
219
-
220
- // Route Handler — route.ts API endpoints
221
- // Design doc: design/07-routing.md §"route.ts — API Endpoints"
222
- export { handleRouteRequest, resolveAllowedMethods } from './route-handler';
223
- export type { RouteModule, RouteHandler, HttpMethod } from './route-handler';
67
+ export { getTraceId, getSpanId, withSpan, addSpanEvent } from './tracing';
224
68
 
225
- // Render timeoutdesign doc: 02-rendering-pipeline.md §"Streaming Constraints"
226
- export { RenderTimeoutError } from './render-timeout';
227
- export type { RenderTimeout } from './render-timeout';
69
+ // Segment paramstyped route param coercion for params.ts convention files.
70
+ // Moved from @timber-js/app/segment-params to here per TIM-723.
71
+ export { defineSegmentParams } from '../segment-params/define.js';
72
+ export type { ParamsDefinition, InferParamField, ParamField } from '../segment-params/define.js';