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

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
@@ -0,0 +1,169 @@
1
+ // #server-internal — Framework-internal server exports
2
+ //
3
+ // This module is a Node package import (#server-internal in package.json
4
+ // "imports" field). It is only resolvable from within @timber-js/app —
5
+ // external consumers cannot import it.
6
+ //
7
+ // These exports are consumed by entry modules (rsc-entry, ssr-entry),
8
+ // adapters, dev server plugins, and build plugins. They are NOT part of
9
+ // the user-facing API and should not appear in documentation or autocomplete
10
+ // for application developers.
11
+ //
12
+ // Design doc: design/triage/api-naming-spike.md §8.5
13
+
14
+ // ── Request Context internals ────────────────────────────────────────────
15
+ export {
16
+ setSegmentParams,
17
+ runWithRequestContext,
18
+ setMutableCookieContext,
19
+ markResponseFlushed,
20
+ getSetCookieHeaders,
21
+ } from './request-context.js';
22
+
23
+ // ── Signal classes (instanceof targets for pipeline catch logic) ──────────
24
+ export { DenySignal, RedirectSignal, RenderError } from './primitives.js';
25
+ export type { RenderErrorDigest } from './primitives.js';
26
+
27
+ // ── Pipeline ─────────────────────────────────────────────────────────────
28
+ export { createPipeline } from './pipeline.js';
29
+ export type {
30
+ PipelineConfig,
31
+ RouteMatch,
32
+ RouteMatcher,
33
+ RouteRenderer,
34
+ EarlyHintsEmitter,
35
+ } from './pipeline.js';
36
+
37
+ // ── Early Hints ──────────────────────────────────────────────────────────
38
+ export { collectEarlyHintHeaders, formatLinkHeader } from './early-hints.js';
39
+ export type { EarlyHint } from './early-hints.js';
40
+
41
+ // ── Early Hints 103 Sender — ALS bridge for platform adapters ────────────
42
+ export { runWithEarlyHintsSender, sendEarlyHints103 } from './early-hints-sender.js';
43
+ export type { EarlyHintsSenderFn } from './early-hints-sender.js';
44
+
45
+ // ── Canonicalization ─────────────────────────────────────────────────────
46
+ export { canonicalize } from './canonicalize.js';
47
+ export type { CanonicalizeResult } from './canonicalize.js';
48
+
49
+ // ── Proxy ────────────────────────────────────────────────────────────────
50
+ export { runProxy } from './proxy.js';
51
+ export type { ProxyFn, ProxyExport } from './proxy.js';
52
+
53
+ // ── Middleware ────────────────────────────────────────────────────────────
54
+ export { runMiddleware } from './middleware-runner.js';
55
+ export type { MiddlewareFn } from './middleware-runner.js';
56
+
57
+ // ── Tree Builder ─────────────────────────────────────────────────────────
58
+ export { buildElementTree } from './tree-builder.js';
59
+ export type {
60
+ TreeBuilderConfig,
61
+ TreeBuildResult,
62
+ LoadedModule,
63
+ ModuleLoader,
64
+ AccessGateProps,
65
+ SlotAccessGateProps,
66
+ ErrorBoundaryProps,
67
+ } from './tree-builder.js';
68
+
69
+ // ── Access Gates ─────────────────────────────────────────────────────────
70
+ export { AccessGate, SlotAccessGate } from './access-gate.js';
71
+
72
+ // ── Status-Code Resolver ─────────────────────────────────────────────────
73
+ export { resolveStatusFile, resolveSlotDenied } from './status-code-resolver.js';
74
+ export type {
75
+ StatusFileResolution,
76
+ StatusFileKind,
77
+ StatusFileFormat,
78
+ SlotDeniedResolution,
79
+ SlotDeniedKind,
80
+ } from './status-code-resolver.js';
81
+
82
+ // ── Flush Controller ─────────────────────────────────────────────────────
83
+ export { flushResponse } from './flush.js';
84
+ export type { FlushOptions, FlushResult, RenderFn, RenderResult } from './flush.js';
85
+
86
+ // ── CSRF Protection ──────────────────────────────────────────────────────
87
+ export { validateCsrf } from './csrf.js';
88
+ export type { CsrfConfig, CsrfResult } from './csrf.js';
89
+
90
+ // ── Body Limits ──────────────────────────────────────────────────────────
91
+ export { parseBodySize, enforceBodyLimits, DEFAULT_LIMITS } from './body-limits.js';
92
+ export type { BodyLimitsConfig, BodyLimitResult, BodyKind } from './body-limits.js';
93
+
94
+ // ── Metadata ─────────────────────────────────────────────────────────────
95
+ export {
96
+ resolveMetadata,
97
+ resolveTitle,
98
+ resolveMetadataUrls,
99
+ renderMetadataToElements,
100
+ } from './metadata.js';
101
+ export type { SegmentMetadataEntry, ResolveMetadataOptions, HeadElement } from './metadata.js';
102
+
103
+ // ── Metadata Routes ──────────────────────────────────────────────────────
104
+ export {
105
+ classifyMetadataRoute,
106
+ getMetadataRouteServePath,
107
+ getMetadataRouteAutoLink,
108
+ METADATA_ROUTE_CONVENTIONS,
109
+ } from './metadata-routes.js';
110
+ export type { MetadataRouteInfo, MetadataRouteType } from './metadata-routes.js';
111
+
112
+ // ── Server Actions (pipeline internals) ──────────────────────────────────
113
+ export { executeAction, buildNoJsResponse, isRscActionRequest } from './actions.js';
114
+ export type {
115
+ RevalidateRenderer,
116
+ RevalidationState,
117
+ ActionHandlerConfig,
118
+ ActionHandlerResult,
119
+ } from './actions.js';
120
+
121
+ // ── Tracing internals ────────────────────────────────────────────────────
122
+ export { runWithTraceId, replaceTraceId, generateTraceId } from './tracing.js';
123
+ export type { TraceStore } from './tracing.js';
124
+
125
+ // ── Logger ───────────────────────────────────────────────────────────────
126
+ export { setLogger, getLogger } from './logger.js';
127
+ export {
128
+ logRequestCompleted,
129
+ logRequestReceived,
130
+ logSlowRequest,
131
+ logMiddlewareShortCircuit,
132
+ logMiddlewareError,
133
+ logRenderError,
134
+ logProxyError,
135
+ logWaitUntilUnsupported,
136
+ logWaitUntilRejected,
137
+ logSwrRefetchFailed,
138
+ logCacheMiss,
139
+ } from './logger.js';
140
+ export type { TimberLogger } from './logger.js';
141
+
142
+ // ── Instrumentation ──────────────────────────────────────────────────────
143
+ export { loadInstrumentation, callOnRequestError, hasOnRequestError } from './instrumentation.js';
144
+ export type {
145
+ InstrumentationOnRequestError,
146
+ InstrumentationRequestInfo,
147
+ InstrumentationErrorContext,
148
+ } from './instrumentation.js';
149
+
150
+ // ── Dev Warnings ─────────────────────────────────────────────────────────
151
+ export {
152
+ warnSuspenseWrappingChildren,
153
+ warnDenyInSuspense,
154
+ warnRedirectInSuspense,
155
+ warnRedirectInAccess,
156
+ warnStaticRequestApi,
157
+ warnSlowSlotWithoutSuspense,
158
+ setViteServer,
159
+ WarningId,
160
+ } from './dev-warnings.js';
161
+ export type { DevWarningConfig } from './dev-warnings.js';
162
+
163
+ // ── Route Handler ────────────────────────────────────────────────────────
164
+ export { handleRouteRequest, resolveAllowedMethods } from './route-handler.js';
165
+ export type { RouteModule, RouteHandler, HttpMethod } from './route-handler.js';
166
+
167
+ // ── Render Timeout ───────────────────────────────────────────────────────
168
+ export { RenderTimeoutError } from './render-timeout.js';
169
+ export type { RenderTimeout } from './render-timeout.js';
@@ -30,7 +30,7 @@ import {
30
30
  replaceTraceId,
31
31
  withSpan,
32
32
  setSpanAttribute,
33
- traceId,
33
+ getTraceId,
34
34
  } from './tracing.js';
35
35
  import {
36
36
  logRequestReceived,
@@ -245,7 +245,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
245
245
  const traceIdValue = generateTraceId();
246
246
 
247
247
  return runWithTraceId(traceIdValue, async () => {
248
- // Establish request context ALS scope so headers() and cookies() work
248
+ // Establish request context ALS scope so getHeaders() and getCookies() work
249
249
  // throughout the entire request lifecycle (proxy, middleware, render).
250
250
  return runWithRequestContext(req, async () => {
251
251
  // In dev mode, wrap with timing collector for Server-Timing header.
@@ -533,7 +533,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
533
533
  }
534
534
 
535
535
  // Store coerced segment params in ALS so components can access them
536
- // via rawSegmentParams() instead of receiving them as a prop.
536
+ // via getSegmentParams() instead of receiving them as a prop.
537
537
  // See design/07-routing.md §"params.ts — Convention File for Typed Params"
538
538
  setSegmentParams(match.segmentParams);
539
539
 
@@ -578,16 +578,21 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
578
578
  applyCookieJar(finalResponse.headers);
579
579
  // Merge parent-set responseHeaders onto the short-circuit response.
580
580
  // Child-set headers take precedence — only add headers not already present.
581
+ // Snapshot existing keys first so multi-value headers (Set-Cookie, Link)
582
+ // from the parent are all appended when the child didn't set that key.
583
+ const existingKeys = new Set(
584
+ [...finalResponse.headers.keys()].map((k) => k.toLowerCase())
585
+ );
581
586
  for (const [key, value] of responseHeaders.entries()) {
582
- if (!finalResponse.headers.has(key)) {
583
- finalResponse.headers.set(key, value);
587
+ if (!existingKeys.has(key.toLowerCase())) {
588
+ finalResponse.headers.append(key, value);
584
589
  }
585
590
  }
586
591
  logMiddlewareShortCircuit({ method, path, status: finalResponse.status });
587
592
  return finalResponse;
588
593
  }
589
594
  // Middleware chain completed without short-circuiting — apply any
590
- // injected request headers so headers() returns them downstream.
595
+ // injected request headers so getHeaders() returns them downstream.
591
596
  applyRequestHeaderOverlay(requestHeaderOverlay);
592
597
  } catch (error) {
593
598
  setMutableCookieContext(false);
@@ -669,7 +674,7 @@ async function fireOnRequestError(
669
674
  await callOnRequestError(
670
675
  error,
671
676
  { method: req.method, path: url.pathname, headers: headersObj },
672
- { phase, routePath: url.pathname, routeType: 'page', traceId: traceId() }
677
+ { phase, routePath: url.pathname, routeType: 'page', traceId: getTraceId() }
673
678
  );
674
679
  }
675
680
 
@@ -145,37 +145,65 @@ export class DenySignal extends Error {
145
145
  }
146
146
  }
147
147
 
148
+ /** Options for deny() when using the object form. */
149
+ export interface DenyOptions {
150
+ /** HTTP status code (4xx or 5xx). Default: 403. */
151
+ status?: number;
152
+ /** Human-readable message (logged server-side, not sent to client). */
153
+ message?: string;
154
+ /** JSON-serializable data passed as `dangerouslyPassData` prop to status-code files. */
155
+ data?: JsonSerializable;
156
+ }
157
+
148
158
  /**
149
- * Universal denial primitive. Throws a `DenySignal` that the framework catches.
159
+ * Universal denial/error primitive. Throws a `DenySignal` that the framework catches.
150
160
  *
151
161
  * - In segment context (outside Suspense): produces HTTP status code
152
162
  * - In slot context: graceful degradation → denied.tsx → default.tsx → null
153
163
  * - Inside Suspense (hold window): promoted to pre-flush behavior
154
164
  * - Inside Suspense (after flush): error boundary + noindex meta
155
165
  *
156
- * @param status - Any 4xx HTTP status code. Defaults to 403.
157
- * @param data - Optional JSON-serializable data passed as `dangerouslyPassData` prop to status-code files.
166
+ * Supports both positional and object signatures:
167
+ * ```ts
168
+ * deny() // 403 (default)
169
+ * deny(404) // 404
170
+ * deny(503, { retry: true }) // 503 with data
171
+ * deny({ status: 503, message: 'Maintenance' }) // object form
172
+ * ```
173
+ *
174
+ * Accepts any 4xx or 5xx status code. This replaces the need for
175
+ * `throw new RenderError(...)` in user code — RenderError is now an
176
+ * internal pipeline detail.
177
+ *
178
+ * @param statusOrOptions - Status code (number) or options object. Default: 403.
179
+ * @param data - Optional JSON-serializable data (positional form only).
158
180
  */
159
- export function deny(status: number = 403, data?: JsonSerializable): never {
160
- if (status < 400 || status > 499) {
161
- throw new Error(
162
- `deny() requires a 4xx status code, got ${status}. ` +
163
- 'For 5xx errors, throw a RenderError instead.'
164
- );
181
+ export function deny(statusOrOptions?: number | DenyOptions, data?: JsonSerializable): never {
182
+ let status: number;
183
+ let resolvedData: JsonSerializable | undefined;
184
+
185
+ if (typeof statusOrOptions === 'object' && statusOrOptions !== null) {
186
+ status = statusOrOptions.status ?? 403;
187
+ resolvedData = statusOrOptions.data;
188
+ } else {
189
+ status = statusOrOptions ?? 403;
190
+ resolvedData = data;
191
+ }
192
+
193
+ if (status < 400 || status > 599) {
194
+ throw new Error(`deny() requires a 4xx or 5xx status code, got ${status}.`);
165
195
  }
166
- warnIfNotSerializable(data, 'deny()');
167
- throw new DenySignal(status, data);
196
+ warnIfNotSerializable(resolvedData, 'deny()');
197
+ throw new DenySignal(status, resolvedData);
168
198
  }
169
199
 
170
200
  /**
171
- * Convenience alias for `deny(404)`.
172
- *
173
- * Provided for Next.js API compatibility — libraries and user code that
174
- * call `notFound()` from `next/navigation` get the same behavior as
175
- * `deny(404)` in timber.
201
+ * @deprecated Use `deny(404)` instead.
202
+ * Kept for internal use by the Next.js shim layer.
203
+ * @internal
176
204
  */
177
205
  export function notFound(): never {
178
- throw new DenySignal(404);
206
+ deny(404);
179
207
  }
180
208
 
181
209
  /**
@@ -215,8 +243,17 @@ const ABSOLUTE_URL_RE = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/)/;
215
243
  * Options for redirect() — alternative to passing a bare status code.
216
244
  */
217
245
  export interface RedirectOptions {
218
- /** HTTP redirect status code (3xx). Defaults to 302. */
246
+ /** HTTP redirect status code (3xx). Defaults to 302 (or 308 when `permanent: true`). */
219
247
  status?: number;
248
+ /**
249
+ * When true, defaults the status to 308 (Permanent Redirect, preserves HTTP method).
250
+ * If `status` is also provided, `status` takes precedence.
251
+ *
252
+ * @example
253
+ * redirect('/new-path', { permanent: true }); // 308
254
+ * redirect('/new-path', { permanent: true, status: 301 }); // 301
255
+ */
256
+ permanent?: boolean;
220
257
  /**
221
258
  * Preserve search params from the current request URL on the redirect target.
222
259
  *
@@ -246,10 +283,18 @@ export interface RedirectOptions {
246
283
  * redirect(`/docs/${version}/${slug}`, { preserveSearchParams: ['foo'] });
247
284
  */
248
285
  export function redirect(path: string, statusOrOptions?: number | RedirectOptions): never {
249
- const status =
250
- typeof statusOrOptions === 'number' ? statusOrOptions : (statusOrOptions?.status ?? 302);
251
- const preserveSearchParams =
252
- typeof statusOrOptions === 'object' ? statusOrOptions.preserveSearchParams : undefined;
286
+ let status: number;
287
+ let preserveSearchParams: true | string[] | undefined;
288
+
289
+ if (typeof statusOrOptions === 'number') {
290
+ status = statusOrOptions;
291
+ } else if (statusOrOptions) {
292
+ // Explicit status wins. Otherwise permanent: true → 308, default → 302.
293
+ status = statusOrOptions.status ?? (statusOrOptions.permanent ? 308 : 302);
294
+ preserveSearchParams = statusOrOptions.preserveSearchParams;
295
+ } else {
296
+ status = 302;
297
+ }
253
298
 
254
299
  if (status < 300 || status > 399) {
255
300
  throw new Error(`redirect() requires a 3xx status code, got ${status}.`);
@@ -271,16 +316,12 @@ export function redirect(path: string, statusOrOptions?: number | RedirectOption
271
316
  }
272
317
 
273
318
  /**
274
- * Permanent redirect to a relative path. Shorthand for `redirect(path, 308)`.
275
- *
276
- * Uses 308 (Permanent Redirect) which preserves the HTTP method — the browser
277
- * will replay POST requests to the new location. This matches Next.js behavior.
278
- *
279
- * @param path - Relative path (e.g. '/new-page', '/dashboard')
280
- * @param options - Optional redirect options (e.g. preserveSearchParams).
319
+ * @deprecated Use `redirect(path, { permanent: true })` instead.
320
+ * Kept for internal use by the Next.js shim layer.
321
+ * @internal
281
322
  */
282
323
  export function permanentRedirect(path: string, options?: Omit<RedirectOptions, 'status'>): never {
283
- redirect(path, { status: 308, ...options });
324
+ redirect(path, { permanent: true, ...options });
284
325
  }
285
326
 
286
327
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Request Context — per-request ALS store for headers() and cookies().
2
+ * Request Context — per-request ALS store for getHeaders() and getCookies().
3
3
  *
4
4
  * Follows the same pattern as tracing.ts: a module-level AsyncLocalStorage
5
5
  * instance, public accessor functions that throw outside request scope,
@@ -12,8 +12,8 @@
12
12
 
13
13
  import { requestContextAls, type RequestContextStore, type CookieEntry } from './als-registry.js';
14
14
  import { isDebug } from './debug.js';
15
- import { _setRawSearchParamsFn } from '../search-params/define.js';
16
- import { _setRawSegmentParamsFn } from '../segment-params/define.js';
15
+ import { _setGetSearchParamsFn } from '../search-params/define.js';
16
+ import { _setGetSegmentParamsFn } from '../segment-params/define.js';
17
17
 
18
18
  // Re-export the ALS for framework-internal consumers that need direct access.
19
19
  export { requestContextAls };
@@ -30,15 +30,34 @@ export { requestContextAls };
30
30
  * Available in middleware, access checks, server components, and server actions.
31
31
  * Throws if called outside a request context (security principle #2: no global fallback).
32
32
  */
33
- export function headers(): ReadonlyHeaders {
33
+ export function getHeaders(): Promise<ReadonlyHeaders> {
34
34
  const store = requestContextAls.getStore();
35
35
  if (!store) {
36
36
  throw new Error(
37
- '[timber] headers() called outside of a request context. ' +
37
+ '[timber] getHeaders() called outside of a request context. ' +
38
38
  'It can only be used in middleware, access checks, server components, and server actions.'
39
39
  );
40
40
  }
41
- return store.headers;
41
+ return Promise.resolve(store.headers);
42
+ }
43
+
44
+ /**
45
+ * Returns the value of a single request header, or undefined if absent.
46
+ *
47
+ * Thin wrapper over `(await getHeaders()).get(name)` for the common
48
+ * case where you need exactly one header.
49
+ *
50
+ * ```ts
51
+ * import { getHeader } from '@timber-js/app/server'
52
+ *
53
+ * export default async function Page() {
54
+ * const auth = await getHeader('authorization');
55
+ * }
56
+ * ```
57
+ */
58
+ export async function getHeader(name: string): Promise<string | undefined> {
59
+ const headers = await getHeaders();
60
+ return headers.get(name) ?? undefined;
42
61
  }
43
62
 
44
63
  /**
@@ -56,11 +75,11 @@ export function headers(): ReadonlyHeaders {
56
75
  *
57
76
  * See design/29-cookies.md
58
77
  */
59
- export function cookies(): RequestCookies {
78
+ export function getCookies(): Promise<RequestCookies> {
60
79
  const store = requestContextAls.getStore();
61
80
  if (!store) {
62
81
  throw new Error(
63
- '[timber] cookies() called outside of a request context. ' +
82
+ '[timber] getCookies() called outside of a request context. ' +
64
83
  'It can only be used in middleware, access checks, server components, and server actions.'
65
84
  );
66
85
  }
@@ -71,7 +90,7 @@ export function cookies(): RequestCookies {
71
90
  }
72
91
 
73
92
  const map = store.parsedCookies;
74
- return {
93
+ return Promise.resolve({
75
94
  get(name: string): string | undefined {
76
95
  return map.get(name);
77
96
  },
@@ -90,7 +109,7 @@ export function cookies(): RequestCookies {
90
109
  if (store.flushed) {
91
110
  if (isDebug()) {
92
111
  console.warn(
93
- `[timber] warn: cookies().set('${name}') called after response headers were committed.\n` +
112
+ `[timber] warn: getCookies().set('${name}') called after response headers were committed.\n` +
94
113
  ` The cookie will NOT be sent. Move cookie mutations to middleware.ts, a server action,\n` +
95
114
  ` or a route.ts handler.`
96
115
  );
@@ -107,7 +126,7 @@ export function cookies(): RequestCookies {
107
126
  assertMutable(store, 'setFromHeaders');
108
127
  if (store.flushed) {
109
128
  console.warn(
110
- `[timber] warn: cookies().setFromHeaders() called after response headers were committed.\n` +
129
+ `[timber] warn: getCookies().setFromHeaders() called after response headers were committed.\n` +
111
130
  ` The cookies will NOT be sent. Move cookie mutations to middleware.ts, a server action,\n` +
112
131
  ` or a route.ts handler.`
113
132
  );
@@ -131,7 +150,7 @@ export function cookies(): RequestCookies {
131
150
  if (store.flushed) {
132
151
  if (isDebug()) {
133
152
  console.warn(
134
- `[timber] warn: cookies().delete('${name}') called after response headers were committed.\n` +
153
+ `[timber] warn: getCookies().delete('${name}') called after response headers were committed.\n` +
135
154
  ` The cookie will NOT be deleted. Move cookie mutations to middleware.ts, a server action,\n` +
136
155
  ` or a route.ts handler.`
137
156
  );
@@ -168,7 +187,26 @@ export function cookies(): RequestCookies {
168
187
  .map(([name, value]) => `${name}=${value}`)
169
188
  .join('; ');
170
189
  },
171
- };
190
+ });
191
+ }
192
+
193
+ /**
194
+ * Returns the value of a single cookie, or undefined if absent.
195
+ *
196
+ * Thin wrapper over `(await getCookies()).get(name)` for the common
197
+ * case where you need exactly one cookie.
198
+ *
199
+ * ```ts
200
+ * import { getCookie } from '@timber-js/app/server'
201
+ *
202
+ * export default async function Page() {
203
+ * const session = await getCookie('session_id');
204
+ * }
205
+ * ```
206
+ */
207
+ export async function getCookie(name: string): Promise<string | undefined> {
208
+ const cookies = await getCookies();
209
+ return cookies.get(name);
172
210
  }
173
211
 
174
212
  /**
@@ -179,40 +217,40 @@ export function cookies(): RequestCookies {
179
217
  *
180
218
  * ```ts
181
219
  * import { searchParams } from './params'
182
- * const parsed = await searchParams.load()
220
+ * const parsed = await searchParams.get()
183
221
  * ```
184
222
  *
185
223
  * Or explicitly:
186
224
  *
187
225
  * ```ts
188
- * import { rawSearchParams } from '@timber-js/app/server'
226
+ * import { getSearchParams } from '@timber-js/app/server'
189
227
  * import { searchParams } from './params'
190
- * const parsed = searchParams.parse(await rawSearchParams())
228
+ * const parsed = searchParams.parse(await getSearchParams())
191
229
  * ```
192
230
  *
193
231
  * Throws if called outside a request context.
194
232
  */
195
- export function rawSearchParams(): Promise<URLSearchParams> {
233
+ export function getSearchParams(): Promise<URLSearchParams> {
196
234
  const store = requestContextAls.getStore();
197
235
  if (!store) {
198
236
  throw new Error(
199
- '[timber] rawSearchParams() called outside of a request context. ' +
237
+ '[timber] getSearchParams() called outside of a request context. ' +
200
238
  'It can only be used in middleware, access checks, server components, and server actions.'
201
239
  );
202
240
  }
203
241
  return store.searchParamsPromise;
204
242
  }
205
243
 
206
- // Eagerly register rawSearchParams with the search-params module so
207
- // searchParams.load() can call it synchronously without a dynamic import.
244
+ // Eagerly register getSearchParams with the search-params module so
245
+ // searchParams.get() can call it synchronously without a dynamic import.
208
246
  // Dynamic imports lose ALS context in React's RSC Flight renderer,
209
- // breaking rawSearchParams() in parallel slot pages. See TIM-523.
210
- _setRawSearchParamsFn(rawSearchParams);
247
+ // breaking getSearchParams() in parallel slot pages. See TIM-523.
248
+ _setGetSearchParamsFn(getSearchParams);
211
249
 
212
- // Eagerly register rawSegmentParams with the segment-params module so
213
- // segmentParams.load() can call it synchronously without a dynamic import.
250
+ // Eagerly register getSegmentParams with the segment-params module so
251
+ // segmentParams.get() can call it synchronously without a dynamic import.
214
252
  // Same pattern as search params — dynamic imports lose ALS context. See TIM-523.
215
- _setRawSegmentParamsFn(rawSegmentParams);
253
+ _setGetSegmentParamsFn(getSegmentParams);
216
254
 
217
255
  /**
218
256
  * Returns a Promise resolving to the current request's coerced segment params.
@@ -225,27 +263,27 @@ _setRawSegmentParamsFn(rawSegmentParams);
225
263
  * This is the primary way page and layout components access route params:
226
264
  *
227
265
  * ```ts
228
- * import { rawSegmentParams } from '@timber-js/app/server'
266
+ * import { getSegmentParams } from '@timber-js/app/server'
229
267
  *
230
268
  * export default async function Page() {
231
- * const { slug } = await rawSegmentParams()
269
+ * const { slug } = await getSegmentParams()
232
270
  * // ...
233
271
  * }
234
272
  * ```
235
273
  *
236
274
  * Throws if called outside a request context.
237
275
  */
238
- export function rawSegmentParams(): Promise<Record<string, string | string[]>> {
276
+ export function getSegmentParams(): Promise<Record<string, string | string[]>> {
239
277
  const store = requestContextAls.getStore();
240
278
  if (!store) {
241
279
  throw new Error(
242
- '[timber] rawSegmentParams() called outside of a request context. ' +
280
+ '[timber] getSegmentParams() called outside of a request context. ' +
243
281
  'It can only be used in middleware, access checks, server components, and server actions.'
244
282
  );
245
283
  }
246
284
  if (!store.segmentParamsPromise) {
247
285
  throw new Error(
248
- '[timber] rawSegmentParams() called before route matching completed. ' +
286
+ '[timber] getSegmentParams() called before route matching completed. ' +
249
287
  'Segment params are not available until after the route is matched.'
250
288
  );
251
289
  }
@@ -402,7 +440,7 @@ function parseSetCookie(
402
440
  }
403
441
 
404
442
  /**
405
- * Cookie accessor returned by `cookies()`.
443
+ * Cookie accessor returned by `getCookies()`.
406
444
  *
407
445
  * Read methods are always available. Mutation methods throw in read-only
408
446
  * contexts (access.ts, server components).
@@ -426,7 +464,7 @@ export interface RequestCookies {
426
464
  * Useful when forwarding cookies from an internal `fetch()` or auth handler:
427
465
  * ```ts
428
466
  * const response = await auth.handler(req);
429
- * cookies().setFromHeaders(response.headers);
467
+ * getCookies().then(c => c.setFromHeaders(response.headers));
430
468
  * ```
431
469
  */
432
470
  setFromHeaders(headers: Headers): void;
@@ -442,7 +480,7 @@ export interface RequestCookies {
442
480
 
443
481
  /**
444
482
  * Run a callback within a request context. Used by the pipeline to establish
445
- * per-request ALS scope so that `headers()` and `cookies()` work.
483
+ * per-request ALS scope so that `getHeaders()` and `getCookies()` work.
446
484
  *
447
485
  * @param req - The incoming Request object.
448
486
  * @param fn - The function to run within the request context.
@@ -492,7 +530,7 @@ export function markResponseFlushed(): void {
492
530
  /**
493
531
  * Build a Map of cookie name → value reflecting the current request's
494
532
  * read-your-own-writes state. Includes incoming cookies plus any
495
- * mutations from cookies().set() / cookies().delete() in the same request.
533
+ * mutations from getCookies().set() / getCookies().delete() in the same request.
496
534
  *
497
535
  * Used by SSR renderers to populate NavContext.cookies so that
498
536
  * useCookie()'s server snapshot matches the actual response state.
@@ -512,8 +550,8 @@ export function getCookiesForSsr(): Map<string, string> {
512
550
  }
513
551
 
514
552
  // The parsedCookies map already reflects read-your-own-writes:
515
- // - cookies().set() updates the map via map.set(name, value)
516
- // - cookies().delete() removes from the map via map.delete(name)
553
+ // - getCookies().set() updates the map via map.set(name, value)
554
+ // - getCookies().delete() removes from the map via map.delete(name)
517
555
  // Return a copy so callers can't mutate the internal map.
518
556
  return new Map(store.parsedCookies);
519
557
  }
@@ -535,7 +573,7 @@ export function getSetCookieHeaders(): string[] {
535
573
  *
536
574
  * Called by the pipeline after middleware.ts runs. Merges overlay headers
537
575
  * on top of the original request headers so downstream code (access.ts,
538
- * server components, server actions) sees them via `headers()`.
576
+ * server components, server actions) sees them via `getHeaders()`.
539
577
  *
540
578
  * The original request headers are never mutated — a new frozen Headers
541
579
  * object is created with the overlay applied on top.
@@ -582,7 +620,7 @@ function freezeHeaders(source: Headers): Headers {
582
620
  if (typeof prop === 'string' && MUTATING_METHODS.has(prop)) {
583
621
  return () => {
584
622
  throw new Error(
585
- `[timber] headers() returns a read-only Headers object. ` +
623
+ `[timber] getHeaders() returns a read-only Headers object. ` +
586
624
  `Calling .${prop}() is not allowed. ` +
587
625
  `Use ctx.requestHeaders in middleware to inject headers for downstream components.`
588
626
  );
@@ -604,7 +642,7 @@ function freezeHeaders(source: Headers): Headers {
604
642
  function assertMutable(store: RequestContextStore, method: string): void {
605
643
  if (!store.mutableContext) {
606
644
  throw new Error(
607
- `[timber] cookies().${method}() cannot be called in this context.\n` +
645
+ `(timber] getCookies().${method}() cannot be called in this context.\n` +
608
646
  ` Set cookies in middleware.ts, server actions, or route.ts handlers.`
609
647
  );
610
648
  }