@timber-js/app 0.2.0-alpha.147 → 0.2.0-alpha.148

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 (274) hide show
  1. package/dist/_chunks/{actions-OIZ8siQu.js → actions-a0RczDnB.js} +8 -34
  2. package/dist/_chunks/{actions-OIZ8siQu.js.map → actions-a0RczDnB.js.map} +1 -1
  3. package/dist/_chunks/cache-api-Co-yI-gP.js +424 -0
  4. package/dist/_chunks/cache-api-Co-yI-gP.js.map +1 -0
  5. package/dist/_chunks/{metadata-routes-BjepjefT.js → canonicalize-P41GR6tY.js} +65 -2
  6. package/dist/_chunks/canonicalize-P41GR6tY.js.map +1 -0
  7. package/dist/_chunks/{cli-schema-sync-CQaxupHv.js → cli-schema-sync-DJDdyEae.js} +111 -260
  8. package/dist/_chunks/cli-schema-sync-DJDdyEae.js.map +1 -0
  9. package/dist/_chunks/cli-schema-sync-D_IhHEUW.js +2 -0
  10. package/dist/_chunks/{define-DMVhsipD.js → define-Bssfp6ot.js} +3 -33
  11. package/dist/_chunks/define-Bssfp6ot.js.map +1 -0
  12. package/dist/_chunks/{define-C3RvHQPK.js → define-CFmvb4Bt.js} +15 -7
  13. package/dist/_chunks/define-CFmvb4Bt.js.map +1 -0
  14. package/dist/_chunks/{href-validation-DyZIzfMJ.js → href-validation-FiA_rToW.js} +2 -1
  15. package/dist/_chunks/{href-validation-DyZIzfMJ.js.map → href-validation-FiA_rToW.js.map} +1 -1
  16. package/dist/_chunks/{tracing-M_zKubhk.js → logger-CM0clugc.js} +394 -107
  17. package/dist/_chunks/logger-CM0clugc.js.map +1 -0
  18. package/dist/_chunks/navigation-root-bUDZLw74.js.map +1 -1
  19. package/dist/_chunks/plugin-context-DurqFBuY.js +2 -0
  20. package/dist/_chunks/plugin-context-GfDWsdd2.js +268 -0
  21. package/dist/_chunks/plugin-context-GfDWsdd2.js.map +1 -0
  22. package/dist/_chunks/{resolve-schema-Dj8tQloi.js → resolve-schema-3iUvBV5T.js} +3 -30
  23. package/dist/_chunks/resolve-schema-3iUvBV5T.js.map +1 -0
  24. package/dist/_chunks/{schema-bridge-DK_89h8O.js → schema-bridge-BY3QLBL7.js} +35 -2
  25. package/dist/_chunks/schema-bridge-BY3QLBL7.js.map +1 -0
  26. package/dist/_chunks/tree-match-D2l830j2.js +102 -0
  27. package/dist/_chunks/tree-match-D2l830j2.js.map +1 -0
  28. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +1 -1
  29. package/dist/_chunks/{walkers-Ck4Y45XN.js → walkers-DvWDj453.js} +107 -82
  30. package/dist/_chunks/walkers-DvWDj453.js.map +1 -0
  31. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -1
  32. package/dist/adapters/cloudflare-kv-cache.js +36 -14
  33. package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
  34. package/dist/adapters/compress-module.d.ts +5 -4
  35. package/dist/adapters/compress-module.d.ts.map +1 -1
  36. package/dist/adapters/nitro.d.ts +1 -3
  37. package/dist/adapters/nitro.d.ts.map +1 -1
  38. package/dist/adapters/nitro.js +14 -134
  39. package/dist/adapters/nitro.js.map +1 -1
  40. package/dist/cache/cache-api.d.ts.map +1 -1
  41. package/dist/cache/index.d.ts +3 -1
  42. package/dist/cache/index.d.ts.map +1 -1
  43. package/dist/cache/index.js +20 -235
  44. package/dist/cache/index.js.map +1 -1
  45. package/dist/cache/invalidation-epoch.d.ts +43 -0
  46. package/dist/cache/invalidation-epoch.d.ts.map +1 -0
  47. package/dist/cache/redis-handler.d.ts +13 -0
  48. package/dist/cache/redis-handler.d.ts.map +1 -1
  49. package/dist/cache/timber-cache.d.ts +17 -9
  50. package/dist/cache/timber-cache.d.ts.map +1 -1
  51. package/dist/cli-schema-sync.d.ts +21 -1
  52. package/dist/cli-schema-sync.d.ts.map +1 -1
  53. package/dist/cli.d.ts +12 -0
  54. package/dist/cli.d.ts.map +1 -1
  55. package/dist/cli.js +26 -31
  56. package/dist/cli.js.map +1 -1
  57. package/dist/client/browser-entry/hmr.d.ts.map +1 -1
  58. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  59. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  60. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  61. package/dist/client/history.d.ts +10 -17
  62. package/dist/client/history.d.ts.map +1 -1
  63. package/dist/client/index.js +1 -1
  64. package/dist/client/index.js.map +1 -1
  65. package/dist/client/internal.js +30 -33
  66. package/dist/client/internal.js.map +1 -1
  67. package/dist/client/navigation-api.d.ts +9 -2
  68. package/dist/client/navigation-api.d.ts.map +1 -1
  69. package/dist/client/navigation-context.d.ts +0 -11
  70. package/dist/client/navigation-context.d.ts.map +1 -1
  71. package/dist/client/navigation-root.d.ts +0 -5
  72. package/dist/client/navigation-root.d.ts.map +1 -1
  73. package/dist/client/router.d.ts.map +1 -1
  74. package/dist/client/rsc-fetch.d.ts.map +1 -1
  75. package/dist/client/use-search-params.d.ts +10 -5
  76. package/dist/client/use-search-params.d.ts.map +1 -1
  77. package/dist/codec.js +1 -1
  78. package/dist/config-validation.d.ts.map +1 -1
  79. package/dist/cookies/define-cookie.d.ts.map +1 -1
  80. package/dist/cookies/index.js +2 -4
  81. package/dist/cookies/index.js.map +1 -1
  82. package/dist/dev-tools/dev-page-shell.d.ts +1 -0
  83. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -1
  84. package/dist/dev-tools/hydration-overlay.d.ts +11 -0
  85. package/dist/dev-tools/hydration-overlay.d.ts.map +1 -0
  86. package/dist/dev-tools/index.d.ts +1 -2
  87. package/dist/dev-tools/index.d.ts.map +1 -1
  88. package/dist/fonts/ast-walk.d.ts +68 -0
  89. package/dist/fonts/ast-walk.d.ts.map +1 -0
  90. package/dist/fonts/ast.d.ts +23 -11
  91. package/dist/fonts/ast.d.ts.map +1 -1
  92. package/dist/fonts/bundle.d.ts +3 -1
  93. package/dist/fonts/bundle.d.ts.map +1 -1
  94. package/dist/fonts/dev-middleware.d.ts +1 -1
  95. package/dist/fonts/dev-middleware.d.ts.map +1 -1
  96. package/dist/fonts/fallbacks.d.ts +0 -7
  97. package/dist/fonts/fallbacks.d.ts.map +1 -1
  98. package/dist/fonts/google.d.ts +4 -2
  99. package/dist/fonts/google.d.ts.map +1 -1
  100. package/dist/fonts/local.d.ts +3 -3
  101. package/dist/fonts/local.d.ts.map +1 -1
  102. package/dist/fonts/paths.d.ts +43 -0
  103. package/dist/fonts/paths.d.ts.map +1 -0
  104. package/dist/fonts/pipeline.d.ts +7 -21
  105. package/dist/fonts/pipeline.d.ts.map +1 -1
  106. package/dist/fonts/transform.d.ts.map +1 -1
  107. package/dist/fonts/virtual-modules.d.ts +1 -18
  108. package/dist/fonts/virtual-modules.d.ts.map +1 -1
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +609 -406
  111. package/dist/index.js.map +1 -1
  112. package/dist/params/index.js +1 -1
  113. package/dist/params/resolve-schema.d.ts.map +1 -1
  114. package/dist/plugin-context.d.ts +28 -5
  115. package/dist/plugin-context.d.ts.map +1 -1
  116. package/dist/plugins/cache.d.ts +13 -5
  117. package/dist/plugins/cache.d.ts.map +1 -1
  118. package/dist/plugins/dev-server.d.ts.map +1 -1
  119. package/dist/plugins/fonts.d.ts +1 -2
  120. package/dist/plugins/fonts.d.ts.map +1 -1
  121. package/dist/plugins/routing.d.ts.map +1 -1
  122. package/dist/routing/codegen.d.ts.map +1 -1
  123. package/dist/routing/convention-lint.d.ts.map +1 -1
  124. package/dist/routing/file-cache.d.ts +8 -0
  125. package/dist/routing/file-cache.d.ts.map +1 -0
  126. package/dist/routing/index.js +2 -2
  127. package/dist/routing/interception.d.ts +0 -5
  128. package/dist/routing/interception.d.ts.map +1 -1
  129. package/dist/routing/manifest-codegen.d.ts +32 -0
  130. package/dist/routing/manifest-codegen.d.ts.map +1 -0
  131. package/dist/routing/scanner.d.ts.map +1 -1
  132. package/dist/schema-bridge.d.ts +15 -0
  133. package/dist/schema-bridge.d.ts.map +1 -1
  134. package/dist/search-params/define.d.ts.map +1 -1
  135. package/dist/search-params/index.js +7 -3
  136. package/dist/search-params/index.js.map +1 -1
  137. package/dist/search-params/wrappers.d.ts.map +1 -1
  138. package/dist/segment-params/define.d.ts.map +1 -1
  139. package/dist/segment-params/index.js +1 -1
  140. package/dist/server/access-gate.d.ts.map +1 -1
  141. package/dist/server/actions.d.ts +0 -14
  142. package/dist/server/actions.d.ts.map +1 -1
  143. package/dist/server/build-manifest.d.ts +0 -29
  144. package/dist/server/build-manifest.d.ts.map +1 -1
  145. package/dist/server/canonicalize.d.ts +10 -0
  146. package/dist/server/canonicalize.d.ts.map +1 -1
  147. package/dist/server/deny-renderer.d.ts +1 -1
  148. package/dist/server/deny-renderer.d.ts.map +1 -1
  149. package/dist/server/html-injector-core.d.ts +8 -0
  150. package/dist/server/html-injector-core.d.ts.map +1 -1
  151. package/dist/server/html-injectors.d.ts.map +1 -1
  152. package/dist/server/index.js +2 -2
  153. package/dist/server/internal.d.ts +1 -5
  154. package/dist/server/internal.d.ts.map +1 -1
  155. package/dist/server/internal.js +12 -171
  156. package/dist/server/internal.js.map +1 -1
  157. package/dist/server/node-stream-transforms.d.ts +0 -11
  158. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  159. package/dist/server/render-utils.d.ts +25 -0
  160. package/dist/server/render-utils.d.ts.map +1 -0
  161. package/dist/server/route-element-builder.d.ts +11 -25
  162. package/dist/server/route-element-builder.d.ts.map +1 -1
  163. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  164. package/dist/server/rsc-entry/helpers.d.ts +2 -18
  165. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  166. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  167. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  168. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  169. package/dist/server/ssr-render.d.ts.map +1 -1
  170. package/dist/shared/href-validation.d.ts.map +1 -1
  171. package/dist/shims/image.d.ts +15 -15
  172. package/package.json +2 -2
  173. package/src/adapters/cloudflare-kv-cache.ts +56 -19
  174. package/src/adapters/compress-module.ts +27 -118
  175. package/src/adapters/nitro.ts +3 -41
  176. package/src/adapters/raw-imports.d.ts +5 -0
  177. package/src/cache/cache-api.ts +9 -1
  178. package/src/cache/index.ts +5 -2
  179. package/src/cache/invalidation-epoch.ts +93 -0
  180. package/src/cache/redis-handler.ts +33 -2
  181. package/src/cache/timber-cache.ts +109 -53
  182. package/src/cli-schema-sync.ts +123 -30
  183. package/src/cli.ts +33 -37
  184. package/src/client/browser-entry/hmr.ts +0 -11
  185. package/src/client/browser-entry/hydrate.ts +39 -5
  186. package/src/client/browser-entry/post-hydration.ts +6 -15
  187. package/src/client/browser-entry/router-init.ts +98 -3
  188. package/src/client/history.ts +10 -24
  189. package/src/client/navigation-api.ts +14 -9
  190. package/src/client/navigation-context.ts +1 -21
  191. package/src/client/navigation-root.tsx +0 -8
  192. package/src/client/router.ts +18 -8
  193. package/src/client/rsc-fetch.ts +7 -2
  194. package/src/client/use-search-params.ts +10 -5
  195. package/src/config-validation.ts +33 -25
  196. package/src/cookies/define-cookie.ts +2 -18
  197. package/src/dev-tools/dev-page-shell.ts +6 -2
  198. package/src/dev-tools/hydration-overlay.ts +158 -0
  199. package/src/dev-tools/index.ts +3 -16
  200. package/src/fonts/ast-walk.ts +235 -0
  201. package/src/fonts/ast.ts +91 -249
  202. package/src/fonts/bundle.ts +16 -9
  203. package/src/fonts/dev-middleware.ts +16 -5
  204. package/src/fonts/fallbacks.ts +0 -23
  205. package/src/fonts/google.ts +7 -3
  206. package/src/fonts/local.ts +7 -6
  207. package/src/fonts/paths.ts +58 -0
  208. package/src/fonts/pipeline.ts +27 -48
  209. package/src/fonts/transform.ts +27 -0
  210. package/src/fonts/virtual-modules.ts +1 -57
  211. package/src/index.ts +5 -11
  212. package/src/params/resolve-schema.ts +2 -34
  213. package/src/plugin-context.ts +95 -17
  214. package/src/plugins/cache.ts +64 -17
  215. package/src/plugins/dev-server.ts +61 -9
  216. package/src/plugins/fonts.ts +21 -6
  217. package/src/plugins/routing.ts +5 -348
  218. package/src/routing/codegen.ts +3 -2
  219. package/src/routing/convention-lint.ts +4 -3
  220. package/src/routing/file-cache.ts +23 -0
  221. package/src/routing/interception.ts +2 -21
  222. package/src/routing/manifest-codegen.ts +290 -0
  223. package/src/routing/scanner.ts +5 -17
  224. package/src/routing/schema-validation.ts +2 -2
  225. package/src/schema-bridge.ts +63 -2
  226. package/src/search-params/define.ts +34 -2
  227. package/src/search-params/wrappers.ts +21 -3
  228. package/src/segment-params/define.ts +2 -62
  229. package/src/server/access-gate.tsx +8 -0
  230. package/src/server/actions.ts +8 -31
  231. package/src/server/build-manifest.ts +0 -56
  232. package/src/server/canonicalize.ts +6 -3
  233. package/src/server/deny-renderer.ts +6 -0
  234. package/src/server/html-injector-core.ts +19 -0
  235. package/src/server/html-injectors.ts +14 -4
  236. package/src/server/internal.ts +1 -18
  237. package/src/server/node-stream-transforms.ts +5 -72
  238. package/src/server/render-utils.ts +31 -0
  239. package/src/server/route-element-builder.ts +77 -47
  240. package/src/server/rsc-entry/error-renderer.ts +1 -0
  241. package/src/server/rsc-entry/helpers.ts +6 -34
  242. package/src/server/rsc-entry/index.ts +11 -0
  243. package/src/server/rsc-entry/render-route.ts +7 -9
  244. package/src/server/rsc-entry/rsc-payload.ts +2 -1
  245. package/src/server/rsc-entry/ssr-renderer.ts +2 -2
  246. package/src/server/ssr-render.ts +1 -11
  247. package/src/shared/href-validation.ts +8 -0
  248. package/dist/_chunks/cli-schema-sync-CBiMEtqa.js +0 -2
  249. package/dist/_chunks/cli-schema-sync-CQaxupHv.js.map +0 -1
  250. package/dist/_chunks/define-C3RvHQPK.js.map +0 -1
  251. package/dist/_chunks/define-DMVhsipD.js.map +0 -1
  252. package/dist/_chunks/handler-store-BVDgyTh7.js +0 -78
  253. package/dist/_chunks/handler-store-BVDgyTh7.js.map +0 -1
  254. package/dist/_chunks/logger-Dt-jQaBO.js +0 -291
  255. package/dist/_chunks/logger-Dt-jQaBO.js.map +0 -1
  256. package/dist/_chunks/metadata-routes-BjepjefT.js.map +0 -1
  257. package/dist/_chunks/resolve-schema-Dj8tQloi.js.map +0 -1
  258. package/dist/_chunks/schema-bridge-DK_89h8O.js.map +0 -1
  259. package/dist/_chunks/tracing-M_zKubhk.js.map +0 -1
  260. package/dist/_chunks/walkers-Ck4Y45XN.js.map +0 -1
  261. package/dist/_chunks/warnings-CNPXaypM.js +0 -125
  262. package/dist/_chunks/warnings-CNPXaypM.js.map +0 -1
  263. package/dist/client/link-pending-store.d.ts +0 -65
  264. package/dist/client/link-pending-store.d.ts.map +0 -1
  265. package/dist/client/nav-link-store.d.ts +0 -27
  266. package/dist/client/nav-link-store.d.ts.map +0 -1
  267. package/dist/dev-tools/warnings.d.ts +0 -109
  268. package/dist/dev-tools/warnings.d.ts.map +0 -1
  269. package/dist/server/flush.d.ts +0 -74
  270. package/dist/server/flush.d.ts.map +0 -1
  271. package/src/client/link-pending-store.ts +0 -116
  272. package/src/client/nav-link-store.ts +0 -37
  273. package/src/dev-tools/warnings.ts +0 -238
  274. package/src/server/flush.ts +0 -183
@@ -1,291 +0,0 @@
1
- import { s as getTraceStore, v as isDebug, y as isDevMode } from "./tracing-M_zKubhk.js";
2
- //#region src/server/error-formatter.ts
3
- /**
4
- * Error Formatter — rewrites SSR/RSC error messages to surface user code.
5
- *
6
- * When React or Vite throw errors during SSR, stack traces reference
7
- * vendored dependency paths (e.g. `.vite/deps_ssr/@vitejs_plugin-rsc_vendor_...`)
8
- * and mangled export names (`__vite_ssr_export_default__`). This module
9
- * rewrites error messages and stack traces to point at user code instead.
10
- *
11
- * Dev-only — in production, errors go through the structured logger
12
- * without formatting.
13
- */
14
- /**
15
- * Patterns that identify internal Vite/RSC vendor paths in stack traces.
16
- * These are replaced with human-readable labels.
17
- */
18
- var VENDOR_PATH_PATTERNS = [
19
- {
20
- pattern: /node_modules\/\.vite\/deps_ssr\/@vitejs_plugin-rsc_vendor_react-server-dom[^\s)]+/g,
21
- replacement: "<react-server-dom>"
22
- },
23
- {
24
- pattern: /node_modules\/\.vite\/deps_ssr\/@vitejs_plugin-rsc_vendor[^\s)]+/g,
25
- replacement: "<rsc-vendor>"
26
- },
27
- {
28
- pattern: /node_modules\/\.vite\/deps_ssr\/[^\s)]+/g,
29
- replacement: "<vite-dep>"
30
- },
31
- {
32
- pattern: /node_modules\/\.vite\/deps\/[^\s)]+/g,
33
- replacement: "<vite-dep>"
34
- }
35
- ];
36
- /**
37
- * Patterns that identify Vite-mangled export names in error messages.
38
- */
39
- var MANGLED_NAME_PATTERNS = [{
40
- pattern: /__vite_ssr_export_default__/g,
41
- replacement: "<default export>"
42
- }, {
43
- pattern: /__vite_ssr_export_(\w+)__/g,
44
- replacement: "<export $1>"
45
- }];
46
- /**
47
- * Rewrite an error's message and stack to replace internal Vite paths
48
- * and mangled names with human-readable labels.
49
- */
50
- function formatSsrError(error) {
51
- if (!(error instanceof Error)) return String(error);
52
- let message = error.message;
53
- let stack = error.stack ?? "";
54
- for (const { pattern, replacement } of MANGLED_NAME_PATTERNS) message = message.replace(pattern, replacement);
55
- for (const { pattern, replacement } of VENDOR_PATH_PATTERNS) stack = stack.replace(pattern, replacement);
56
- for (const { pattern, replacement } of MANGLED_NAME_PATTERNS) stack = stack.replace(pattern, replacement);
57
- const hint = extractErrorHint(error.message);
58
- const parts = [];
59
- parts.push(message);
60
- if (hint) parts.push(` → ${hint}`);
61
- const userFrames = extractUserFrames(stack);
62
- if (userFrames.length > 0) {
63
- parts.push("");
64
- parts.push(" User code in stack:");
65
- for (const frame of userFrames) parts.push(` ${frame}`);
66
- }
67
- return parts.join("\n");
68
- }
69
- /**
70
- * Extract a human-readable hint from common React/RSC error messages.
71
- *
72
- * React error messages contain useful information but the surrounding
73
- * context (vendor paths, mangled names) obscures it. This extracts the
74
- * actionable part as a one-line hint.
75
- */
76
- function extractErrorHint(message) {
77
- if (message.match(/Functions cannot be passed directly to Client Components/)) {
78
- const propMatch = message.match(/<[^>]*?\s(\w+)=\{function/);
79
- if (propMatch) return `Prop "${propMatch[1]}" is a function — mark it "use server" or call it before passing`;
80
- return "A function prop was passed to a Client Component — mark it \"use server\" or call it before passing";
81
- }
82
- if (message.includes("Objects are not valid as a React child")) return "An object was rendered as JSX children — convert to string or extract the value";
83
- const nullRefMatch = message.match(/Cannot read propert(?:y|ies) of (undefined|null) \(reading '(\w+)'\)/);
84
- if (nullRefMatch) return `Accessed .${nullRefMatch[2]} on ${nullRefMatch[1]} — check that the value exists`;
85
- const notFnMatch = message.match(/(\w+) is not a function/);
86
- if (notFnMatch) return `"${notFnMatch[1]}" is not a function — check imports and exports`;
87
- if (message.includes("Element type is invalid")) return "A component resolved to undefined/null — check default exports and import paths";
88
- if (message.includes("Invalid hook call")) return "A hook was called outside of a React component render. If this is a 'use client' component, ensure the directive is at the very top of the file (before any imports) and that @vitejs/plugin-rsc is loaded correctly. Barrel re-exports from non-'use client' files do not propagate the directive.";
89
- return null;
90
- }
91
- /**
92
- * Extract stack frames that reference user code (not node_modules,
93
- * not framework internals).
94
- *
95
- * Returns at most 5 frames to keep output concise.
96
- */
97
- function extractUserFrames(stack) {
98
- const lines = stack.split("\n");
99
- const userFrames = [];
100
- for (const line of lines) {
101
- const trimmed = line.trim();
102
- if (!trimmed.startsWith("at ")) continue;
103
- if (trimmed.includes("node_modules") || trimmed.includes("<react-server-dom>") || trimmed.includes("<rsc-vendor>") || trimmed.includes("<vite-dep>") || trimmed.includes("node:internal")) continue;
104
- userFrames.push(trimmed);
105
- if (userFrames.length >= 5) break;
106
- }
107
- return userFrames;
108
- }
109
- //#endregion
110
- //#region src/server/default-logger.ts
111
- /**
112
- * DefaultLogger — human-readable stderr logging when no custom logger is configured.
113
- *
114
- * Ships as the fallback so production deployments always have error visibility,
115
- * even without an `instrumentation.ts` logger export. Output is one line per
116
- * event, designed for `fly logs`, `kubectl logs`, Cloudflare dashboard tails, etc.
117
- *
118
- * Format:
119
- * [timber] ERROR message key=value key=value trace_id=4bf92f35
120
- * [timber] WARN message key=value key=value trace_id=4bf92f35
121
- * [timber] INFO message method=GET path=/dashboard status=200 durationMs=43 trace_id=4bf92f35
122
- *
123
- * Behavior:
124
- * - Suppressed entirely in dev mode (dev logging handles all output)
125
- * - `debug` suppressed unless TIMBER_DEBUG is set
126
- * - Replaced entirely when a custom logger is set via `setLogger()`
127
- *
128
- * See design/17-logging.md §"DefaultLogger"
129
- */
130
- /**
131
- * Format data fields as `key=value` pairs for human-readable output.
132
- * - `error` key is serialized via formatSsrError for stack trace cleanup
133
- * - `trace_id` is truncated to 8 chars for readability (full ID in OTEL)
134
- * - Other values are stringified inline
135
- */
136
- function formatDataFields(data) {
137
- if (!data) return "";
138
- const parts = [];
139
- let traceId;
140
- for (const [key, value] of Object.entries(data)) {
141
- if (key === "trace_id") {
142
- traceId = typeof value === "string" ? value : String(value);
143
- continue;
144
- }
145
- if (key === "error") {
146
- parts.push(`error=${formatSsrError(value)}`);
147
- continue;
148
- }
149
- if (value === void 0 || value === null) continue;
150
- parts.push(`${key}=${value}`);
151
- }
152
- if (traceId) parts.push(`trace_id=${traceId.slice(0, 8)}`);
153
- return parts.length > 0 ? " " + parts.join(" ") : "";
154
- }
155
- /** Pad level string to fixed width for alignment. */
156
- function padLevel(level) {
157
- return level.padEnd(5);
158
- }
159
- function createDefaultLogger() {
160
- return {
161
- error(msg, data) {
162
- const fields = formatDataFields(data);
163
- process.stderr.write(`[timber] ${padLevel("ERROR")} ${msg}${fields}\n`);
164
- },
165
- warn(msg, data) {
166
- const fields = formatDataFields(data);
167
- process.stderr.write(`[timber] ${padLevel("WARN")} ${msg}${fields}\n`);
168
- },
169
- info(msg, data) {
170
- if (isDevMode()) return;
171
- if (!isDebug()) return;
172
- const fields = formatDataFields(data);
173
- process.stderr.write(`[timber] ${padLevel("INFO")} ${msg}${fields}\n`);
174
- },
175
- debug(msg, data) {
176
- if (isDevMode()) return;
177
- if (!isDebug()) return;
178
- const fields = formatDataFields(data);
179
- process.stderr.write(`[timber] ${padLevel("DEBUG")} ${msg}${fields}\n`);
180
- }
181
- };
182
- }
183
- //#endregion
184
- //#region src/server/logger.ts
185
- /**
186
- * Logger — structured logging with environment-aware formatting.
187
- *
188
- * timber.js ships a DefaultLogger that writes human-readable lines to stderr
189
- * in production. Users can export a custom logger from instrumentation.ts to
190
- * replace it with pino, winston, or any TimberLogger-compatible object.
191
- *
192
- * See design/17-logging.md §"Production Logging"
193
- */
194
- var _logger = createDefaultLogger();
195
- /**
196
- * Set the user-provided logger. Called by the instrumentation loader
197
- * when it finds a `logger` export in instrumentation.ts. Replaces
198
- * the DefaultLogger entirely.
199
- */
200
- function setLogger(logger) {
201
- _logger = logger;
202
- }
203
- /**
204
- * Get the current logger. Always non-null — returns DefaultLogger when
205
- * no custom logger is configured.
206
- */
207
- function getLogger() {
208
- return _logger;
209
- }
210
- /**
211
- * Inject trace_id and span_id into log data for log–trace correlation.
212
- * Always injects trace_id (never undefined). Injects span_id only when OTEL is active.
213
- */
214
- function withTraceContext(data) {
215
- const store = getTraceStore();
216
- const enriched = { ...data };
217
- if (store) {
218
- enriched.trace_id = store.traceId;
219
- if (store.spanId) enriched.span_id = store.spanId;
220
- }
221
- return enriched;
222
- }
223
- /** Log a completed request. Level: info. */
224
- function logRequestCompleted(data) {
225
- _logger.info("request completed", withTraceContext(data));
226
- }
227
- /** Log request received. Level: debug. */
228
- function logRequestReceived(data) {
229
- _logger.debug("request received", withTraceContext(data));
230
- }
231
- /** Log a slow request warning. Level: warn. */
232
- function logSlowRequest(data) {
233
- _logger.warn("slow request exceeded threshold", withTraceContext(data));
234
- }
235
- /** Log middleware short-circuit. Level: debug. */
236
- function logMiddlewareShortCircuit(data) {
237
- _logger.debug("middleware short-circuited", withTraceContext(data));
238
- }
239
- /** Log unhandled error in middleware phase. Level: error. */
240
- function logMiddlewareError(data) {
241
- _logger.error("unhandled error in middleware phase", withTraceContext(data));
242
- }
243
- /** Log unhandled render-phase error. Level: error. */
244
- function logRenderError(data) {
245
- _logger.error("unhandled render-phase error", withTraceContext(data));
246
- }
247
- /** Log proxy.ts uncaught error. Level: error. */
248
- function logProxyError(data) {
249
- _logger.error("proxy.ts threw uncaught error", withTraceContext(data));
250
- }
251
- /** Log unhandled error in route handler. Level: error. */
252
- function logRouteError(data) {
253
- _logger.error("unhandled route handler error", withTraceContext(data));
254
- }
255
- /** Log waitUntil() adapter missing (once at startup). Level: warn. */
256
- function logWaitUntilUnsupported() {
257
- _logger.warn("adapter does not support waitUntil()");
258
- }
259
- /** Log waitUntil() promise rejection. Level: warn. */
260
- function logWaitUntilRejected(data) {
261
- _logger.warn("waitUntil() promise rejected", withTraceContext(data));
262
- }
263
- /** Log staleWhileRevalidate refetch failure. Level: warn. */
264
- function logSwrRefetchFailed(data) {
265
- _logger.warn("staleWhileRevalidate refetch failed", withTraceContext(data));
266
- }
267
- /** Log cache miss. Level: debug. */
268
- function logCacheMiss(data) {
269
- _logger.debug("timber.cache MISS", withTraceContext(data));
270
- }
271
- /**
272
- * Log an intentionally swallowed error. Provides observability into catch
273
- * blocks that are deliberately empty — the error is consumed, never rethrown.
274
- *
275
- * Default level: `warn` in dev (so the overlay surfaces patterns), `debug`
276
- * in production (low noise unless TIMBER_DEBUG is set). Pass `opts.level`
277
- * to override.
278
- *
279
- * **Infallible** — swallow() itself never throws, even if the logger is
280
- * broken. A thrown swallow would turn a benign catch into a crash.
281
- */
282
- function swallow(err, reason, opts) {
283
- try {
284
- const level = opts?.level ?? (isDevMode() ? "warn" : "debug");
285
- _logger[level](`swallowed: ${reason}`, withTraceContext({ error: err }));
286
- } catch {}
287
- }
288
- //#endregion
289
- export { logProxyError as a, logRequestReceived as c, logSwrRefetchFailed as d, logWaitUntilRejected as f, swallow as h, logMiddlewareShortCircuit as i, logRouteError as l, setLogger as m, logCacheMiss as n, logRenderError as o, logWaitUntilUnsupported as p, logMiddlewareError as r, logRequestCompleted as s, getLogger as t, logSlowRequest as u };
290
-
291
- //# sourceMappingURL=logger-Dt-jQaBO.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logger-Dt-jQaBO.js","names":[],"sources":["../../src/server/error-formatter.ts","../../src/server/default-logger.ts","../../src/server/logger.ts"],"sourcesContent":["/**\n * Error Formatter — rewrites SSR/RSC error messages to surface user code.\n *\n * When React or Vite throw errors during SSR, stack traces reference\n * vendored dependency paths (e.g. `.vite/deps_ssr/@vitejs_plugin-rsc_vendor_...`)\n * and mangled export names (`__vite_ssr_export_default__`). This module\n * rewrites error messages and stack traces to point at user code instead.\n *\n * Dev-only — in production, errors go through the structured logger\n * without formatting.\n */\n\n// ─── Stack Trace Rewriting ──────────────────────────────────────────────────\n\n/**\n * Patterns that identify internal Vite/RSC vendor paths in stack traces.\n * These are replaced with human-readable labels.\n */\nconst VENDOR_PATH_PATTERNS: Array<{ pattern: RegExp; replacement: string }> = [\n {\n pattern: /node_modules\\/\\.vite\\/deps_ssr\\/@vitejs_plugin-rsc_vendor_react-server-dom[^\\s)]+/g,\n replacement: '<react-server-dom>',\n },\n {\n pattern: /node_modules\\/\\.vite\\/deps_ssr\\/@vitejs_plugin-rsc_vendor[^\\s)]+/g,\n replacement: '<rsc-vendor>',\n },\n {\n pattern: /node_modules\\/\\.vite\\/deps_ssr\\/[^\\s)]+/g,\n replacement: '<vite-dep>',\n },\n {\n pattern: /node_modules\\/\\.vite\\/deps\\/[^\\s)]+/g,\n replacement: '<vite-dep>',\n },\n];\n\n/**\n * Patterns that identify Vite-mangled export names in error messages.\n */\nconst MANGLED_NAME_PATTERNS: Array<{ pattern: RegExp; replacement: string }> = [\n {\n pattern: /__vite_ssr_export_default__/g,\n replacement: '<default export>',\n },\n {\n pattern: /__vite_ssr_export_(\\w+)__/g,\n replacement: '<export $1>',\n },\n];\n\n/**\n * Rewrite an error's message and stack to replace internal Vite paths\n * and mangled names with human-readable labels.\n */\nexport function formatSsrError(error: unknown): string {\n if (!(error instanceof Error)) {\n return String(error);\n }\n\n let message = error.message;\n let stack = error.stack ?? '';\n\n // Rewrite mangled names in the message\n for (const { pattern, replacement } of MANGLED_NAME_PATTERNS) {\n message = message.replace(pattern, replacement);\n }\n\n // Rewrite vendor paths in the stack\n for (const { pattern, replacement } of VENDOR_PATH_PATTERNS) {\n stack = stack.replace(pattern, replacement);\n }\n\n // Rewrite mangled names in the stack too\n for (const { pattern, replacement } of MANGLED_NAME_PATTERNS) {\n stack = stack.replace(pattern, replacement);\n }\n\n // Extract hints from React-specific error patterns\n const hint = extractErrorHint(error.message);\n\n // Build formatted output: cleaned message, hint (if any), then cleaned stack\n const parts: string[] = [];\n parts.push(message);\n if (hint) {\n parts.push(` → ${hint}`);\n }\n\n // Include only the user-code frames from the stack (skip the first line\n // which is the message itself, and filter out vendor-only frames)\n const userFrames = extractUserFrames(stack);\n if (userFrames.length > 0) {\n parts.push('');\n parts.push(' User code in stack:');\n for (const frame of userFrames) {\n parts.push(` ${frame}`);\n }\n }\n\n return parts.join('\\n');\n}\n\n// ─── Error Hint Extraction ──────────────────────────────────────────────────\n\n/**\n * Extract a human-readable hint from common React/RSC error messages.\n *\n * React error messages contain useful information but the surrounding\n * context (vendor paths, mangled names) obscures it. This extracts the\n * actionable part as a one-line hint.\n */\nfunction extractErrorHint(message: string): string | null {\n // \"Functions cannot be passed directly to Client Components\"\n // Extract the component and prop name from the JSX-like syntax in the message\n const fnPassedMatch = message.match(/Functions cannot be passed directly to Client Components/);\n if (fnPassedMatch) {\n // Try to extract the prop name from the message\n // React formats: <... propName={function ...} ...>\n const propMatch = message.match(/<[^>]*?\\s(\\w+)=\\{function/);\n if (propMatch) {\n return `Prop \"${propMatch[1]}\" is a function — mark it \"use server\" or call it before passing`;\n }\n return 'A function prop was passed to a Client Component — mark it \"use server\" or call it before passing';\n }\n\n // \"Objects are not valid as a React child\"\n if (message.includes('Objects are not valid as a React child')) {\n return 'An object was rendered as JSX children — convert to string or extract the value';\n }\n\n // \"Cannot read properties of undefined/null\"\n const nullRefMatch = message.match(\n /Cannot read propert(?:y|ies) of (undefined|null) \\(reading '(\\w+)'\\)/\n );\n if (nullRefMatch) {\n return `Accessed .${nullRefMatch[2]} on ${nullRefMatch[1]} — check that the value exists`;\n }\n\n // \"X is not a function\"\n const notFnMatch = message.match(/(\\w+) is not a function/);\n if (notFnMatch) {\n return `\"${notFnMatch[1]}\" is not a function — check imports and exports`;\n }\n\n // \"Element type is invalid\"\n if (message.includes('Element type is invalid')) {\n return 'A component resolved to undefined/null — check default exports and import paths';\n }\n\n // \"Invalid hook call\" — hooks called outside React's render context.\n // In RSC, this typically means a 'use client' component was executed as a\n // server component instead of being serialized as a client reference.\n if (message.includes('Invalid hook call')) {\n return (\n 'A hook was called outside of a React component render. ' +\n \"If this is a 'use client' component, ensure the directive is at the very top of the file \" +\n '(before any imports) and that @vitejs/plugin-rsc is loaded correctly. ' +\n \"Barrel re-exports from non-'use client' files do not propagate the directive.\"\n );\n }\n\n return null;\n}\n\n// ─── Stack Frame Filtering ──────────────────────────────────────────────────\n\n/**\n * Extract stack frames that reference user code (not node_modules,\n * not framework internals).\n *\n * Returns at most 5 frames to keep output concise.\n */\nfunction extractUserFrames(stack: string): string[] {\n const lines = stack.split('\\n');\n const userFrames: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n // Skip non-frame lines\n if (!trimmed.startsWith('at ')) continue;\n // Skip node_modules, vendor, and internal frames\n if (\n trimmed.includes('node_modules') ||\n trimmed.includes('<react-server-dom>') ||\n trimmed.includes('<rsc-vendor>') ||\n trimmed.includes('<vite-dep>') ||\n trimmed.includes('node:internal')\n ) {\n continue;\n }\n userFrames.push(trimmed);\n if (userFrames.length >= 5) break;\n }\n\n return userFrames;\n}\n","/**\n * DefaultLogger — human-readable stderr logging when no custom logger is configured.\n *\n * Ships as the fallback so production deployments always have error visibility,\n * even without an `instrumentation.ts` logger export. Output is one line per\n * event, designed for `fly logs`, `kubectl logs`, Cloudflare dashboard tails, etc.\n *\n * Format:\n * [timber] ERROR message key=value key=value trace_id=4bf92f35\n * [timber] WARN message key=value key=value trace_id=4bf92f35\n * [timber] INFO message method=GET path=/dashboard status=200 durationMs=43 trace_id=4bf92f35\n *\n * Behavior:\n * - Suppressed entirely in dev mode (dev logging handles all output)\n * - `debug` suppressed unless TIMBER_DEBUG is set\n * - Replaced entirely when a custom logger is set via `setLogger()`\n *\n * See design/17-logging.md §\"DefaultLogger\"\n */\n\nimport { isDevMode, isDebug } from './debug.js';\nimport { formatSsrError } from './error-formatter.js';\nimport type { TimberLogger } from './logger.js';\n\n/**\n * Format data fields as `key=value` pairs for human-readable output.\n * - `error` key is serialized via formatSsrError for stack trace cleanup\n * - `trace_id` is truncated to 8 chars for readability (full ID in OTEL)\n * - Other values are stringified inline\n */\nfunction formatDataFields(data?: Record<string, unknown>): string {\n if (!data) return '';\n\n const parts: string[] = [];\n let traceId: string | undefined;\n\n for (const [key, value] of Object.entries(data)) {\n if (key === 'trace_id') {\n // Defer trace_id to the end\n traceId = typeof value === 'string' ? value : String(value);\n continue;\n }\n if (key === 'error') {\n // Serialize errors with formatSsrError for clean output\n parts.push(`error=${formatSsrError(value)}`);\n continue;\n }\n if (value === undefined || value === null) continue;\n parts.push(`${key}=${value}`);\n }\n\n // trace_id always last, truncated to 8 chars for readability\n if (traceId) {\n parts.push(`trace_id=${traceId.slice(0, 8)}`);\n }\n\n return parts.length > 0 ? ' ' + parts.join(' ') : '';\n}\n\n/** Pad level string to fixed width for alignment. */\nfunction padLevel(level: string): string {\n return level.padEnd(5);\n}\n\nexport function createDefaultLogger(): TimberLogger {\n return {\n error(msg: string, data?: Record<string, unknown>): void {\n // Errors are ALWAYS logged, including dev mode. Suppressing errors\n // in dev causes silent 500s with no stack trace, making route.ts\n // and render errors impossible to debug. See TIM-555.\n const fields = formatDataFields(data);\n process.stderr.write(`[timber] ${padLevel('ERROR')} ${msg}${fields}\\n`);\n },\n\n warn(msg: string, data?: Record<string, unknown>): void {\n // Warnings are always logged — same rationale as errors.\n const fields = formatDataFields(data);\n process.stderr.write(`[timber] ${padLevel('WARN')} ${msg}${fields}\\n`);\n },\n\n info(msg: string, data?: Record<string, unknown>): void {\n // info is suppressed by default — per-request lines are too noisy\n // without a custom logger. Enable with TIMBER_DEBUG.\n if (isDevMode()) return;\n if (!isDebug()) return;\n const fields = formatDataFields(data);\n process.stderr.write(`[timber] ${padLevel('INFO')} ${msg}${fields}\\n`);\n },\n\n debug(msg: string, data?: Record<string, unknown>): void {\n // debug is suppressed in dev (dev logger handles it) and in\n // production unless TIMBER_DEBUG is explicitly set.\n if (isDevMode()) return;\n if (!isDebug()) return;\n const fields = formatDataFields(data);\n process.stderr.write(`[timber] ${padLevel('DEBUG')} ${msg}${fields}\\n`);\n },\n };\n}\n","/**\n * Logger — structured logging with environment-aware formatting.\n *\n * timber.js ships a DefaultLogger that writes human-readable lines to stderr\n * in production. Users can export a custom logger from instrumentation.ts to\n * replace it with pino, winston, or any TimberLogger-compatible object.\n *\n * See design/17-logging.md §\"Production Logging\"\n */\n\nimport { getTraceStore } from './tracing.js';\nimport { createDefaultLogger } from './default-logger.js';\nimport { isDevMode } from './debug.js';\n\n// ─── Logger Interface ─────────────────────────────────────────────────────\n\n/** Any object with standard log methods satisfies this — pino, winston, consola, console. */\nexport interface TimberLogger {\n info(msg: string, data?: Record<string, unknown>): void;\n warn(msg: string, data?: Record<string, unknown>): void;\n error(msg: string, data?: Record<string, unknown>): void;\n debug(msg: string, data?: Record<string, unknown>): void;\n}\n\n// ─── Logger Registry ──────────────────────────────────────────────────────\n\n// Initialize with DefaultLogger so production errors are never silent.\n// Replaced when setLogger() is called from instrumentation.ts.\nlet _logger: TimberLogger = createDefaultLogger();\n\n/**\n * Set the user-provided logger. Called by the instrumentation loader\n * when it finds a `logger` export in instrumentation.ts. Replaces\n * the DefaultLogger entirely.\n */\nexport function setLogger(logger: TimberLogger): void {\n _logger = logger;\n}\n\n/**\n * Get the current logger. Always non-null — returns DefaultLogger when\n * no custom logger is configured.\n */\nexport function getLogger(): TimberLogger {\n return _logger;\n}\n\n// ─── Framework Log Helpers ────────────────────────────────────────────────\n\n/**\n * Inject trace_id and span_id into log data for log–trace correlation.\n * Always injects trace_id (never undefined). Injects span_id only when OTEL is active.\n */\nfunction withTraceContext(data?: Record<string, unknown>): Record<string, unknown> {\n const store = getTraceStore();\n const enriched: Record<string, unknown> = { ...data };\n if (store) {\n enriched.trace_id = store.traceId;\n if (store.spanId) {\n enriched.span_id = store.spanId;\n }\n }\n return enriched;\n}\n\n// ─── Framework Event Emitters ─────────────────────────────────────────────\n\n/** Log a completed request. Level: info. */\nexport function logRequestCompleted(data: {\n method: string;\n path: string;\n status: number;\n durationMs: number;\n /** Number of concurrent in-flight requests (including this one) at completion time. */\n concurrency?: number;\n}): void {\n _logger.info('request completed', withTraceContext(data));\n}\n\n/** Log request received. Level: debug. */\nexport function logRequestReceived(data: { method: string; path: string }): void {\n _logger.debug('request received', withTraceContext(data));\n}\n\n/** Log a slow request warning. Level: warn. */\nexport function logSlowRequest(data: {\n method: string;\n path: string;\n durationMs: number;\n threshold: number;\n /** Number of concurrent in-flight requests at the time the slow request completed. */\n concurrency?: number;\n}): void {\n _logger.warn('slow request exceeded threshold', withTraceContext(data));\n}\n\n/** Log middleware short-circuit. Level: debug. */\nexport function logMiddlewareShortCircuit(data: {\n method: string;\n path: string;\n status: number;\n}): void {\n _logger.debug('middleware short-circuited', withTraceContext(data));\n}\n\n/** Log unhandled error in middleware phase. Level: error. */\nexport function logMiddlewareError(data: { method: string; path: string; error: unknown }): void {\n _logger.error('unhandled error in middleware phase', withTraceContext(data));\n}\n\n/** Log unhandled render-phase error. Level: error. */\nexport function logRenderError(data: {\n method: string;\n path: string;\n error: unknown;\n errorId?: string;\n}): void {\n _logger.error('unhandled render-phase error', withTraceContext(data));\n}\n\n/** Log proxy.ts uncaught error. Level: error. */\nexport function logProxyError(data: { error: unknown }): void {\n _logger.error('proxy.ts threw uncaught error', withTraceContext(data));\n}\n\n/** Log unhandled error in server action. Level: error. */\nexport function logActionError(data: { method: string; path: string; error: unknown }): void {\n _logger.error('unhandled server action error', withTraceContext(data));\n}\n\n/** Log unhandled error in route handler. Level: error. */\nexport function logRouteError(data: { method: string; path: string; error: unknown }): void {\n _logger.error('unhandled route handler error', withTraceContext(data));\n}\n\n/** Log SSR streaming error (post-shell). Level: error. */\nexport function logStreamingError(data: { error: unknown }): void {\n _logger.error('SSR streaming error (post-shell)', withTraceContext(data));\n}\n\n/** Log waitUntil() adapter missing (once at startup). Level: warn. */\nexport function logWaitUntilUnsupported(): void {\n _logger.warn('adapter does not support waitUntil()');\n}\n\n/** Log waitUntil() promise rejection. Level: warn. */\nexport function logWaitUntilRejected(data: { error: unknown }): void {\n _logger.warn('waitUntil() promise rejected', withTraceContext(data));\n}\n\n/** Log staleWhileRevalidate refetch failure. Level: warn. */\nexport function logSwrRefetchFailed(data: { cacheKey: string; error: unknown }): void {\n _logger.warn('staleWhileRevalidate refetch failed', withTraceContext(data));\n}\n\n/** Log cache miss. Level: debug. */\nexport function logCacheMiss(data: { cacheKey: string }): void {\n _logger.debug('timber.cache MISS', withTraceContext(data));\n}\n\n// ─── Swallow Helper ───────────────────────────────────────────────────────\n\n/**\n * Log an intentionally swallowed error. Provides observability into catch\n * blocks that are deliberately empty — the error is consumed, never rethrown.\n *\n * Default level: `warn` in dev (so the overlay surfaces patterns), `debug`\n * in production (low noise unless TIMBER_DEBUG is set). Pass `opts.level`\n * to override.\n *\n * **Infallible** — swallow() itself never throws, even if the logger is\n * broken. A thrown swallow would turn a benign catch into a crash.\n */\nexport function swallow(err: unknown, reason: string, opts?: { level?: 'debug' | 'warn' }): void {\n try {\n const level = opts?.level ?? (isDevMode() ? 'warn' : 'debug');\n _logger[level](`swallowed: ${reason}`, withTraceContext({ error: err }));\n } catch {\n // swallow() must never throw.\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"metadata-routes-BjepjefT.js","names":[],"sources":["../../src/server/metadata-routes.ts"],"sourcesContent":["/**\n * Metadata route classification for timber.js.\n *\n * Metadata routes are file-based endpoints that generate well-known URLs for\n * crawlers and browsers (sitemap.xml, robots.txt, OG images, etc.).\n *\n * These routes run through proxy.ts but NOT through middleware.ts or access.ts —\n * they are public endpoints by nature.\n *\n * See design/16-metadata.md §\"Metadata Routes\"\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Classification of a metadata route file. */\nexport interface MetadataRouteInfo {\n /** The metadata route type. */\n type: MetadataRouteType;\n /** The content type to serve this route with. */\n contentType: string;\n /** Whether this route can appear in nested segments (not just app root). */\n nestable: boolean;\n}\n\nexport type MetadataRouteType =\n | 'sitemap'\n | 'robots'\n | 'manifest'\n | 'favicon'\n | 'icon'\n | 'opengraph-image'\n | 'apple-icon';\n\n// ─── Convention Table ────────────────────────────────────────────────────────\n\n/**\n * All recognized metadata route file conventions.\n *\n * Each entry maps a base file name (without extension) to its route info.\n * The extensions determine whether the file is static or dynamic.\n *\n * Static extensions: .xml, .txt, .json, .png, .jpg, .ico, .svg\n * Dynamic extensions: .ts, .tsx\n */\nexport const METADATA_ROUTE_CONVENTIONS: Record<\n string,\n {\n type: MetadataRouteType;\n contentType: string;\n nestable: boolean;\n staticExtensions: string[];\n dynamicExtensions: string[];\n /** The URL path this file serves at (relative to segment). */\n servePath: string;\n }\n> = {\n 'sitemap': {\n type: 'sitemap',\n contentType: 'application/xml',\n nestable: true,\n staticExtensions: ['xml'],\n dynamicExtensions: ['ts'],\n servePath: 'sitemap.xml',\n },\n 'robots': {\n type: 'robots',\n contentType: 'text/plain',\n nestable: false,\n staticExtensions: ['txt'],\n dynamicExtensions: ['ts'],\n servePath: 'robots.txt',\n },\n 'manifest': {\n type: 'manifest',\n contentType: 'application/manifest+json',\n nestable: false,\n staticExtensions: ['json'],\n dynamicExtensions: ['ts'],\n servePath: 'manifest.webmanifest',\n },\n 'favicon': {\n type: 'favicon',\n contentType: 'image/x-icon',\n nestable: false,\n staticExtensions: ['ico'],\n dynamicExtensions: ['ts', 'tsx'],\n servePath: 'favicon.ico',\n },\n 'icon': {\n type: 'icon',\n contentType: 'image/*',\n nestable: true,\n staticExtensions: ['png', 'jpg', 'svg'],\n dynamicExtensions: ['ts', 'tsx'],\n servePath: 'icon',\n },\n 'opengraph-image': {\n type: 'opengraph-image',\n contentType: 'image/*',\n nestable: true,\n staticExtensions: ['png', 'jpg'],\n dynamicExtensions: ['ts', 'tsx'],\n servePath: 'opengraph-image',\n },\n\n 'apple-icon': {\n type: 'apple-icon',\n contentType: 'image/*',\n nestable: true,\n staticExtensions: ['png'],\n dynamicExtensions: ['ts', 'tsx'],\n servePath: 'apple-icon',\n },\n};\n\n// ─── MIME Type Resolution ─────────────────────────────────────────────────────\n\n/**\n * Map of file extensions to MIME types for static metadata route files.\n * Used to resolve the generic `image/*` content type for static image files.\n */\nconst EXTENSION_MIME_TYPES: Record<string, string> = {\n xml: 'application/xml',\n txt: 'text/plain',\n json: 'application/json',\n ico: 'image/x-icon',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n svg: 'image/svg+xml',\n webp: 'image/webp',\n};\n\n/**\n * Resolve the concrete MIME type for a static metadata route file.\n *\n * For generic content types like `image/*`, this resolves to the actual\n * MIME type based on the file extension (e.g. `image/png` for `.png`).\n *\n * @param conventionContentType - The content type from the convention table (may be generic like `image/*`)\n * @param extension - The file extension without leading dot (e.g. \"png\", \"xml\")\n * @returns The resolved MIME type\n */\nexport function resolveStaticContentType(conventionContentType: string, extension: string): string {\n if (conventionContentType.includes('*')) {\n return EXTENSION_MIME_TYPES[extension] ?? 'application/octet-stream';\n }\n return conventionContentType;\n}\n\n/**\n * Check if a file extension represents a static (non-code) metadata route file.\n *\n * @param baseName - The base file name without extension (e.g. \"sitemap\", \"icon\")\n * @param extension - The file extension without leading dot (e.g. \"xml\", \"png\", \"ts\")\n * @returns true if this is a static file, false if dynamic or unrecognized\n */\nexport function isStaticMetadataExtension(baseName: string, extension: string): boolean {\n const convention = METADATA_ROUTE_CONVENTIONS[baseName];\n if (!convention) return false;\n return convention.staticExtensions.includes(extension);\n}\n\n/**\n * Check if a file extension represents a dynamic (code) metadata route file.\n *\n * @param baseName - The base file name without extension (e.g. \"sitemap\", \"icon\")\n * @param extension - The file extension without leading dot (e.g. \"ts\", \"tsx\")\n * @returns true if this is a dynamic file, false if static or unrecognized\n */\nexport function isDynamicMetadataExtension(baseName: string, extension: string): boolean {\n const convention = METADATA_ROUTE_CONVENTIONS[baseName];\n if (!convention) return false;\n return convention.dynamicExtensions.includes(extension);\n}\n\n// ─── Classification ──────────────────────────────────────────────────────────\n\n/**\n * Classify a file name as a metadata route, or return null if it's not one.\n *\n * @param fileName - The full file name including extension (e.g. \"sitemap.xml\", \"icon.tsx\")\n * @returns Classification info, or null if not a metadata route\n */\nexport function classifyMetadataRoute(fileName: string): MetadataRouteInfo | null {\n const dotIndex = fileName.lastIndexOf('.');\n if (dotIndex === -1) return null;\n\n const baseName = fileName.slice(0, dotIndex);\n const ext = fileName.slice(dotIndex + 1);\n\n const convention = METADATA_ROUTE_CONVENTIONS[baseName];\n if (!convention) return null;\n\n const isStatic = convention.staticExtensions.includes(ext);\n const isDynamic = convention.dynamicExtensions.includes(ext);\n\n if (!isStatic && !isDynamic) return null;\n\n return {\n type: convention.type,\n contentType: convention.contentType,\n nestable: convention.nestable,\n };\n}\n\n/**\n * Get the serve path for a metadata route type.\n *\n * @param type - The metadata route type\n * @returns The URL path fragment this route serves at\n */\nexport function getMetadataRouteServePath(type: MetadataRouteType): string {\n for (const convention of Object.values(METADATA_ROUTE_CONVENTIONS)) {\n if (convention.type === type) return convention.servePath;\n }\n throw new Error(`[timber] Unknown metadata route type: ${type}`);\n}\n\nconst METADATA_SERVE_PATHS = new Set(\n Object.values(METADATA_ROUTE_CONVENTIONS).map((c) => c.servePath)\n);\n\nexport function isMetadataRouteServePath(pathname: string): boolean {\n const lastSegment = pathname.slice(pathname.lastIndexOf('/') + 1);\n return METADATA_SERVE_PATHS.has(lastSegment);\n}\n\n/** A <link> auto-link tag. */\nexport interface AutoLinkLink {\n tag: 'link';\n rel: string;\n href: string;\n type?: string;\n}\n\n/** A <meta> auto-link tag. */\nexport interface AutoLinkMeta {\n tag: 'meta';\n property?: string;\n name?: string;\n content: string;\n}\n\nexport type AutoLinkTag = AutoLinkLink | AutoLinkMeta;\n\n/**\n * Get the auto-link tags to inject into <head> for metadata route files\n * discovered in a segment.\n *\n * Returns link tags for icon/apple-icon/manifest, and meta tags for\n * opengraph-image (emits both og:image and twitter:image). Returns null\n * for types that don't auto-link (favicon, sitemap, robots).\n *\n * @param type - The metadata route type\n * @param href - The resolved URL path to the metadata route\n * @returns Tag descriptor(s) for the <head>, or null if no auto-link\n */\nexport function getMetadataRouteAutoLink(type: MetadataRouteType, href: string): AutoLinkTag[] {\n switch (type) {\n case 'icon':\n return [{ tag: 'link', rel: 'icon', href }];\n case 'apple-icon':\n return [{ tag: 'link', rel: 'apple-touch-icon', href }];\n case 'manifest':\n return [{ tag: 'link', rel: 'manifest', href }];\n case 'opengraph-image':\n return [\n { tag: 'meta', property: 'og:image', content: href },\n { tag: 'meta', name: 'twitter:image', content: href },\n ];\n default:\n return [];\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolve-schema-Dj8tQloi.js","names":[],"sources":["../../src/params/resolve-schema.ts"],"sourcesContent":["/**\n * Schema resolution — resolves the global schema's segmentParams into\n * a runtime-ready codec map keyed by bracket name (e.g. '[id]').\n *\n * Bracket keys are preserved so that `[id]`, `[...id]`, and `[[...id]]`\n * remain distinct entries. The coercion layer reconstructs bracket keys\n * from matched segment metadata to look up the correct codec.\n *\n * Design doc: design/41-global-params.md §Pipeline Integration\n */\n\nimport type { Codec } from '../codec.js';\nimport { isCodec, isStandardSchema, validateSync } from '../schema-bridge.js';\nimport type { StandardSchemaV1 } from '../schema-bridge.js';\n\n/**\n * Wrap a Standard Schema into a Codec for route params.\n * Parse throws on failure (invalid param → 404). Serialize returns string.\n */\nfunction fromParamSchema<T>(fieldName: string, schema: StandardSchemaV1<T>): Codec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n const result = validateSync(schema, value);\n if (!result.issues) {\n return result.value;\n }\n const messages = result.issues.map((i) => i.message).join(', ');\n throw new Error(`[timber] Param '${fieldName}' coercion failed: ${messages}`);\n },\n serialize(value: T): string | null {\n if (value === null || value === undefined) return null;\n if (Array.isArray(value)) return value.join('/');\n return String(value);\n },\n };\n}\n\n/**\n * Resolve a schema entry to a Codec. Accepts Codec<T> or StandardSchemaV1<T>.\n */\nfunction resolveCodec(fieldName: string, value: unknown): Codec<unknown> {\n if (isCodec(value)) return value;\n if (isStandardSchema(value)) return fromParamSchema(fieldName, value);\n throw new Error(`[timber] Schema field '${fieldName}' is not a valid codec or Standard Schema.`);\n}\n\n/**\n * Reconstruct the bracket key for a segment from its type and param name.\n * Used by the coercion layer to look up codecs in the bracket-keyed map.\n *\n * 'dynamic' + 'id' → '[id]'\n * 'catch-all' + 'slug' → '[...slug]'\n * 'optional-catch-all' + 'slug' → '[[...slug]]'\n */\nexport function toBracketKey(segmentType: string, paramName: string): string {\n switch (segmentType) {\n case 'catch-all':\n return `[...${paramName}]`;\n case 'optional-catch-all':\n return `[[...${paramName}]]`;\n default:\n return `[${paramName}]`;\n }\n}\n\nconst CATCH_ALL_RE = /^\\[\\.\\.\\..+\\]$/;\nconst OPTIONAL_CATCH_ALL_RE = /^\\[\\[\\.\\.\\..+\\]\\]$/;\n\nfunction isCatchAllKey(key: string): boolean {\n return CATCH_ALL_RE.test(key) || OPTIONAL_CATCH_ALL_RE.test(key);\n}\n\nfunction isOptionalCatchAllKey(key: string): boolean {\n return OPTIONAL_CATCH_ALL_RE.test(key);\n}\n\n/**\n * Resolve the global schema's segmentParams into a map of bracket-keyed\n * param name → resolved Codec.\n *\n * Keys are preserved exactly as written in `app/schema.ts` so that\n * `[id]`, `[...id]`, and `[[...id]]` remain distinct entries.\n * The coercion layer in `param-coercion.ts` reconstructs bracket keys\n * from matched segment metadata to look up the correct codec.\n *\n * Input: { '[id]': z.coerce.number(), '[...slug]': codec.string }\n * Output: { '[id]': Codec<number>, '[...slug]': Codec<string> }\n */\nexport function resolveSchemaCodecs(\n segmentParams: Record<string, unknown>\n): Record<string, Codec<unknown>> {\n const resolved: Record<string, Codec<unknown>> = Object.create(null);\n\n for (const [bracketName, value] of Object.entries(segmentParams)) {\n if (isCatchAllKey(bracketName) && isCodec(value) && !('__catchAll' in value)) {\n throw new Error(\n `[timber] Schema error: '${bracketName}' is a catch-all segment and requires an array codec ` +\n `(e.g. codec.catchAll(codec.string)). Got a scalar codec.`\n );\n }\n if (\n isOptionalCatchAllKey(bracketName) &&\n isCodec(value) &&\n '__catchAll' in value &&\n !('__optional' in value)\n ) {\n throw new Error(\n `[timber] Schema error: '${bracketName}' is an optional catch-all segment and requires codec.optionalCatchAll() ` +\n `(e.g. codec.optionalCatchAll(codec.string)). Got a non-optional catch-all codec.`\n );\n }\n resolved[bracketName] = resolveCodec(bracketName, value);\n }\n\n return resolved;\n}\n"],"mappings":";;;;;;AAmBA,SAAS,gBAAmB,WAAmB,QAAuC;CACpF,OAAO;EACL,MAAM,OAAyC;GAC7C,MAAM,SAAS,aAAa,QAAQ,KAAK;GACzC,IAAI,CAAC,OAAO,QACV,OAAO,OAAO;GAEhB,MAAM,WAAW,OAAO,OAAO,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI;GAC9D,MAAM,IAAI,MAAM,mBAAmB,UAAU,qBAAqB,UAAU;EAC9E;EACA,UAAU,OAAyB;GACjC,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;GAClD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,KAAK,GAAG;GAC/C,OAAO,OAAO,KAAK;EACrB;CACF;AACF;;;;AAKA,SAAS,aAAa,WAAmB,OAAgC;CACvE,IAAI,QAAQ,KAAK,GAAG,OAAO;CAC3B,IAAI,iBAAiB,KAAK,GAAG,OAAO,gBAAgB,WAAW,KAAK;CACpE,MAAM,IAAI,MAAM,0BAA0B,UAAU,2CAA2C;AACjG;;;;;;;;;AAUA,SAAgB,aAAa,aAAqB,WAA2B;CAC3E,QAAQ,aAAR;EACE,KAAK,aACH,OAAO,OAAO,UAAU;EAC1B,KAAK,sBACH,OAAO,QAAQ,UAAU;EAC3B,SACE,OAAO,IAAI,UAAU;CACzB;AACF;AAEA,IAAM,eAAe;AACrB,IAAM,wBAAwB;AAE9B,SAAS,cAAc,KAAsB;CAC3C,OAAO,aAAa,KAAK,GAAG,KAAK,sBAAsB,KAAK,GAAG;AACjE;AAEA,SAAS,sBAAsB,KAAsB;CACnD,OAAO,sBAAsB,KAAK,GAAG;AACvC;;;;;;;;;;;;;AAcA,SAAgB,oBACd,eACgC;CAChC,MAAM,WAA2C,OAAO,OAAO,IAAI;CAEnE,KAAK,MAAM,CAAC,aAAa,UAAU,OAAO,QAAQ,aAAa,GAAG;EAChE,IAAI,cAAc,WAAW,KAAK,QAAQ,KAAK,KAAK,EAAE,gBAAgB,QACpE,MAAM,IAAI,MACR,2BAA2B,YAAY,8GAEzC;EAEF,IACE,sBAAsB,WAAW,KACjC,QAAQ,KAAK,KACb,gBAAgB,SAChB,EAAE,gBAAgB,QAElB,MAAM,IAAI,MACR,2BAA2B,YAAY,0JAEzC;EAEF,SAAS,eAAe,aAAa,aAAa,KAAK;CACzD;CAEA,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-bridge-DK_89h8O.js","names":[],"sources":["../../src/schema-bridge.ts"],"sourcesContent":["/**\n * Standard Schema bridge — shared helpers for bridging Standard Schema-compatible\n * validation libraries (Zod, Valibot, ArkType) to the Codec<T> protocol.\n *\n * This module is the single source of truth for:\n * - StandardSchemaV1 interface (subset of the Standard Schema spec)\n * - validateSync() helper\n * - fromSchema() — bridge from Standard Schema to Codec<T>\n * - fromArraySchema() — bridge for array-valued codecs\n *\n * These are re-exported from @timber-js/app/search-params, @timber-js/app/segment-params,\n * and @timber-js/app/cookies for convenience. The canonical import is\n * @timber-js/app/codec.\n *\n * Design doc: design/23a-search-params-triage.md §\"Unify Codec<T> type\"\n */\n\nimport type { Codec } from './codec.js';\n\n// ---------------------------------------------------------------------------\n// Standard Schema interface (subset)\n//\n// Standard Schema (https://github.com/standard-schema/standard-schema) defines\n// a minimal interface that Zod ≥3.24, Valibot ≥1.0, and ArkType all implement.\n// We depend only on `~standard.validate` to avoid coupling to any specific lib.\n// ---------------------------------------------------------------------------\n\n/** Minimal Standard Schema interface for auto-detection. */\nexport interface StandardSchemaV1<Output = unknown> {\n '~standard': {\n validate(value: unknown): StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n };\n}\n\nexport type StandardSchemaResult<Output> =\n | { value: Output; issues?: undefined }\n | { value?: undefined; issues: ReadonlyArray<{ message: string }> };\n\n// ---------------------------------------------------------------------------\n// Sync validate helper\n// ---------------------------------------------------------------------------\n\n/**\n * Run a Standard Schema's `~standard.validate()` synchronously.\n *\n * Zod v4's signature includes `Promise` in the return union to satisfy the\n * Standard Schema spec, but in practice Zod always validates synchronously\n * for the schema types we use. We assert the result is sync and throw if\n * it isn't — codec parsing must be synchronous.\n */\nexport function validateSync<Output>(\n schema: StandardSchemaV1<Output>,\n value: unknown\n): StandardSchemaResult<Output> {\n const result = schema['~standard'].validate(value);\n if (result instanceof Promise) {\n throw new Error(\n '[timber] fromSchema: schema returned a Promise — only sync schemas are supported.'\n );\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\n/** Check if a value is a Standard Schema object. */\nexport function isStandardSchema(value: unknown): value is StandardSchemaV1 {\n return (\n typeof value === 'object' &&\n value !== null &&\n '~standard' in value &&\n typeof (value as StandardSchemaV1)['~standard']?.validate === 'function'\n );\n}\n\n/** Check if a value is a Codec (has parse + serialize methods). */\nexport function isCodec(value: unknown): value is Codec<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Codec<unknown>).parse === 'function' &&\n typeof (value as Codec<unknown>).serialize === 'function'\n );\n}\n\n// ---------------------------------------------------------------------------\n// fromSchema — bridge from Standard Schema to Codec<T>\n// ---------------------------------------------------------------------------\n\n/**\n * Bridge a Standard Schema-compatible schema (Zod, Valibot, ArkType) to a\n * Codec<T>.\n *\n * Parse: coerces the raw string through the schema. On validation failure,\n * parses `undefined` to get the schema's default value (the schema should have\n * a `.default()` call). If that also fails, returns `undefined`.\n *\n * Serialize: uses `String()` for primitives, `null` for null/undefined.\n *\n * ```ts\n * import { fromSchema } from '@timber-js/app/codec'\n * import { z } from 'zod/v4'\n *\n * const pageCodec = fromSchema(z.coerce.number().int().min(1).default(1))\n * ```\n */\nexport function fromSchema<T>(schema: StandardSchemaV1<T>): Codec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n // For array inputs (duplicate query keys), use the first value.\n // Browsers and URLSearchParams.get() return the first occurrence.\n const input = Array.isArray(value) ? value[0] : value;\n\n // Try parsing the raw value\n const result = validateSync(schema, input);\n if (!result.issues) {\n return result.value;\n }\n\n // On failure, try parsing undefined to get the default\n const defaultResult = validateSync(schema, undefined);\n if (!defaultResult.issues) {\n return defaultResult.value;\n }\n\n // No default available — return undefined (codec design choice)\n return undefined as T;\n },\n\n serialize(value: T): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n return String(value);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fromArraySchema — bridge for array-valued codecs\n// ---------------------------------------------------------------------------\n\n/**\n * Bridge a Standard Schema for array values. Handles both single strings\n * and repeated query keys (`?tag=a&tag=b`).\n *\n * ```ts\n * import { fromArraySchema } from '@timber-js/app/codec'\n * import { z } from 'zod/v4'\n *\n * const tagsCodec = fromArraySchema(z.array(z.string()).default([]))\n * ```\n */\nexport function fromArraySchema<T>(schema: StandardSchemaV1<T>): Codec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n // Coerce single string to array for array schemas\n let input: unknown = value;\n if (typeof value === 'string') {\n input = [value];\n } else if (value === undefined) {\n input = undefined;\n }\n\n const result = validateSync(schema, input);\n if (!result.issues) {\n return result.value;\n }\n\n // On failure, try undefined for default\n const defaultResult = validateSync(schema, undefined);\n if (!defaultResult.issues) {\n return defaultResult.value;\n }\n\n return undefined as T;\n },\n\n serialize(value: T): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (Array.isArray(value)) {\n return value.length === 0 ? null : value.join(',');\n }\n return String(value);\n },\n };\n}\n"],"mappings":";;;;;;;;;AAkDA,SAAgB,aACd,QACA,OAC8B;CAC9B,MAAM,SAAS,OAAO,aAAa,SAAS,KAAK;CACjD,IAAI,kBAAkB,SACpB,MAAM,IAAI,MACR,mFACF;CAEF,OAAO;AACT;;AAOA,SAAgB,iBAAiB,OAA2C;CAC1E,OACE,OAAO,UAAU,YACjB,UAAU,QACV,eAAe,SACf,OAAQ,MAA2B,cAAc,aAAa;AAElE;;AAGA,SAAgB,QAAQ,OAAyC;CAC/D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAyB,UAAU,cAC3C,OAAQ,MAAyB,cAAc;AAEnD;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,WAAc,QAAuC;CACnE,OAAO;EACL,MAAM,OAAyC;GAM7C,MAAM,SAAS,aAAa,QAHd,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,KAGP;GACzC,IAAI,CAAC,OAAO,QACV,OAAO,OAAO;GAIhB,MAAM,gBAAgB,aAAa,QAAQ,KAAA,CAAS;GACpD,IAAI,CAAC,cAAc,QACjB,OAAO,cAAc;EAKzB;EAEA,UAAU,OAAyB;GACjC,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;GAET,OAAO,OAAO,KAAK;EACrB;CACF;AACF;;;;;;;;;;;;AAiBA,SAAgB,gBAAmB,QAAuC;CACxE,OAAO;EACL,MAAM,OAAyC;GAE7C,IAAI,QAAiB;GACrB,IAAI,OAAO,UAAU,UACnB,QAAQ,CAAC,KAAK;QACT,IAAI,UAAU,KAAA,GACnB,QAAQ,KAAA;GAGV,MAAM,SAAS,aAAa,QAAQ,KAAK;GACzC,IAAI,CAAC,OAAO,QACV,OAAO,OAAO;GAIhB,MAAM,gBAAgB,aAAa,QAAQ,KAAA,CAAS;GACpD,IAAI,CAAC,cAAc,QACjB,OAAO,cAAc;EAIzB;EAEA,UAAU,OAAyB;GACjC,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;GAET,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,KAAK,GAAG;GAEnD,OAAO,OAAO,KAAK;EACrB;CACF;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tracing-M_zKubhk.js","names":[],"sources":["../../src/server/debug.ts","../../src/server/als-registry.ts","../../src/server/tracing.ts"],"sourcesContent":["/**\n * Runtime debug flag for timber.js.\n *\n * Two distinct functions for two distinct security levels:\n *\n * ## `isDebug()` — server-side logging only\n *\n * Returns true when timber's debug/warning messages should be written to\n * stderr / the server console. This NEVER affects what is sent to the\n * client (no error details, no timing headers, no stack traces).\n *\n * Active when any of:\n * - `NODE_ENV !== 'production'` (standard dev mode)\n * - `TIMBER_DEBUG` env var is set to a truthy value at runtime\n * - `timber.config.ts` has `debug: true`\n *\n * ## `isDevMode()` — client-visible dev behavior\n *\n * Returns true ONLY when `NODE_ENV !== 'production'`. This gates anything\n * that changes what clients can observe:\n * - Dev error pages with stack traces (fallback-error.ts)\n * - Detailed Server-Timing headers (pipeline.ts)\n * - Error messages in action INTERNAL_ERROR payloads (action-client.ts)\n * - Pipeline error handler wiring (Vite overlay)\n *\n * `isDevMode()` is statically replaced in production builds → the guarded\n * code is tree-shaken to zero bytes. TIMBER_DEBUG cannot enable it.\n *\n * Usage:\n * In Cloudflare Workers wrangler.toml:\n * [vars]\n * TIMBER_DEBUG = \"1\"\n *\n * In Node.js:\n * TIMBER_DEBUG=1 node server.js\n *\n * In timber.config.ts:\n * export default { debug: true }\n *\n * See design/13-security.md for the security taxonomy.\n * See design/18-build-system.md for build pipeline details.\n */\n\n// ─── Dev Mode (client-visible) ──────────────────────────────────────────────\n\n/**\n * Check if the application is running in development mode.\n *\n * This is the ONLY function that should gate client-visible dev behavior:\n * - Dev error pages with stack traces\n * - Server-Timing mode default (`'detailed'` in dev, `'total'` in prod)\n * - Error messages in action `INTERNAL_ERROR` payloads\n * - Pipeline error handler wiring (Vite overlay)\n *\n * Returns `process.env.NODE_ENV !== 'production'`, which is statically\n * replaced by the bundler in production builds. Code guarded by this\n * function is tree-shaken to zero bytes in production.\n *\n * TIMBER_DEBUG does NOT enable this — that would leak server internals\n * to clients. Use `isDebug()` for server-side-only logging.\n */\nexport function isDevMode(): boolean {\n return process.env.NODE_ENV !== 'production';\n}\n\n// ─── Debug Flag (server-side logging only) ──────────────────────────────────\n\n/**\n * Config-level debug override. Set via `setDebugFromConfig()` during\n * initialization when timber.config.ts has `debug: true`.\n */\nlet _configDebug = false;\n\n/**\n * Set the debug flag from timber.config.ts.\n * Called during handler initialization.\n */\nexport function setDebugFromConfig(debug: boolean): void {\n _configDebug = debug;\n}\n\n/**\n * Check if timber debug logging is active (server-side only).\n *\n * Returns true if ANY of these conditions hold:\n * - NODE_ENV is not 'production' (standard dev mode)\n * - TIMBER_DEBUG environment variable is set to a truthy value at runtime\n * - timber.config.ts has `debug: true`\n *\n * This function controls ONLY server-side logging — messages written to\n * stderr or the server console. It NEVER affects client-visible behavior\n * (error pages, response headers, action payloads). For client-visible\n * behavior, use `isDevMode()`.\n *\n * The TIMBER_DEBUG check is deliberately written as a dynamic property\n * access so bundlers cannot statically replace it.\n */\nexport function isDebug(): boolean {\n // Fast path: dev mode (statically replaced to `true` in dev, `false` in prod)\n if (process.env.NODE_ENV !== 'production') return true;\n\n // Config override\n if (_configDebug) return true;\n\n // Runtime env var check — uses dynamic access to prevent static replacement.\n // In production builds, process.env.NODE_ENV is statically replaced, but\n // TIMBER_DEBUG must survive as a runtime check. The dynamic key access\n // pattern ensures the bundler treats this as opaque.\n return _readTimberDebugEnv();\n}\n\n/**\n * Read TIMBER_DEBUG from the environment at runtime.\n *\n * Extracted to a separate function to:\n * 1. Prevent bundler inlining (cross-module function calls are not inlined)\n * 2. Handle platforms where `process` may not exist (Cloudflare Workers)\n * 3. Support globalThis.__TIMBER_DEBUG for programmatic control\n */\nfunction _readTimberDebugEnv(): boolean {\n // globalThis override — useful for programmatic control and testing\n if ((globalThis as Record<string, unknown>).__TIMBER_DEBUG) return true;\n\n // process.env — works in Node.js and platforms that polyfill process\n try {\n const key = 'TIMBER_DEBUG';\n const val =\n typeof process !== 'undefined' && process.env\n ? (process.env as Record<string, string | undefined>)[key]\n : undefined;\n if (val && val !== '0' && val !== 'false') return true;\n } catch {\n // process may not exist or env may throw — safe to ignore\n }\n\n return false;\n}\n","/**\n * Centralized AsyncLocalStorage registry for server-side per-request state.\n *\n * ALL ALS instances used by the server framework live here. Individual\n * modules (request-context.ts, tracing.ts, actions.ts, etc.) import from\n * this registry and re-export public accessor functions.\n *\n * Why: ALS instances require singleton semantics — if two copies of the\n * same ALS exist (one from a relative import, one from a barrel import,\n * or the same file re-evaluated by Vite after an HMR invalidation), one\n * module writes to its copy and another reads from an empty copy.\n * Centralizing ALS creation in a single module + anchoring each instance\n * on `globalThis` under a `Symbol.for(...)` key eliminates this class of\n * bug, including the Vite-dev case where module instance split would\n * otherwise hand two copies of this file out of a single evaluation cycle.\n *\n * The `timber-shims` plugin remaps `@timber-js/app/server` to src/ in\n * RSC and SSR environments so import-specifier paths converge here, and\n * the `globalThis[SYMBOL_FOR]` anchor below closes the remaining gap: any\n * second evaluation of this module picks up the ALS the first evaluation\n * already stored rather than instantiating a fresh one. Same pattern the\n * Cloudflare bindings ALS uses in `adapters/cloudflare.ts`.\n *\n * DO NOT create ALS instances outside this file. If you need a new ALS,\n * add it here and import from `./als-registry.js` in the consuming module.\n * Inside this file, always route through `getOrCreateAls(symbol)` — a raw\n * `new AsyncLocalStorage()` reintroduces the instance-split hazard.\n *\n * See design/18-build-system.md §\"Module Singleton Strategy\" and\n * §\"Singleton State Registry\". Regression test:\n * `tests/als-registry-singleton.test.ts`.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { DebugComponentEntry } from './rsc-entry/helpers.js';\n\n/**\n * Return a process-wide singleton `AsyncLocalStorage` keyed by `symbol`.\n *\n * `Symbol.for(...)` keys live in the cross-realm symbol registry, and\n * `globalThis` is shared across every module evaluation inside the same\n * Node.js process. Together they give a stable anchor that survives\n * re-evaluation of this file — the second evaluation observes the\n * `globalThis[symbol]` slot already populated by the first and hands\n * back the identical ALS instance.\n */\nfunction getOrCreateAls<T>(symbol: symbol): AsyncLocalStorage<T> {\n const g = globalThis as unknown as Record<symbol, unknown>;\n const existing = g[symbol];\n if (existing instanceof AsyncLocalStorage) {\n return existing as AsyncLocalStorage<T>;\n }\n const created = new AsyncLocalStorage<T>();\n g[symbol] = created;\n return created;\n}\n\n// ─── Request Context ──────────────────────────────────────────────────────\n// Used by: request-context.ts (getHeaders(), getCookies(), getSearchParams())\n// Design doc: design/04-authorization.md\n\n/** @internal — import via request-context.ts public API */\nexport const requestContextAls = getOrCreateAls<RequestContextStore>(\n Symbol.for('timber:request-context-als')\n);\n\nexport interface RequestContextStore {\n /** Incoming request headers (read-only view). */\n headers: Headers;\n /** Raw cookie header string, parsed lazily into a Map on first access. */\n cookieHeader: string;\n /** Lazily-parsed cookie map (mutable — reflects write-overlay from set()). */\n parsedCookies?: Map<string, string>;\n /** Original (pre-overlay) frozen headers, kept for overlay merging. */\n originalHeaders: Headers;\n /**\n * Raw URLSearchParams for the current request.\n * To get typed parsed params, import a search params definition and\n * call `.parse(searchParams())`.\n */\n searchParams: URLSearchParams;\n /**\n * Raw search string from the request URL (e.g. \"?foo=bar&baz=1\").\n * Available synchronously for use in `redirect()` with `preserveSearchParams`.\n */\n searchString: string;\n /**\n * Coerced segment params for the current request.\n * Set by the pipeline after route matching and param coercion, before\n * middleware and rendering. Pages and layouts read params via\n * `getSegmentParams()` instead of receiving them as a prop.\n *\n * See design/07-routing.md §\"params.ts — Convention File for Typed Params\"\n */\n segmentParams?: Record<string, string | string[]>;\n /**\n * The matched segment path (e.g. '/(browse)/[artistSlug]/[year]').\n * Includes route groups and parallel slots — uniquely identifies the\n * segment in the route tree. Set by the pipeline alongside segmentParams.\n * Used by getSegmentParams() for dev-mode validation.\n * See design/41-global-params.md §Runtime Validation\n */\n matchedSegmentPath?: string;\n /**\n * Per-slot coerced segment params, keyed by the slot's full tree path\n * (e.g. '/(browse)/@shows/[artistSlug]/[...year]').\n *\n * Each slot walks its own segment tree and may interpret URL parts\n * differently from the main route (e.g. catch-all vs dynamic). This\n * map stores slot-specific params so getSegmentParams(segmentPath)\n * returns the correct types for slot pages.\n *\n * See design/07-routing.md §\"Parallel Slot Params\"\n */\n slotParamsMap?: Map<string, Record<string, string | string[]>>;\n /** Outgoing Set-Cookie entries (name → serialized value + options). Last write wins. */\n cookieJar: Map<string, CookieEntry>;\n /** Whether the response has flushed (headers committed). */\n flushed: boolean;\n /** Whether the current context allows cookie mutation. */\n mutableContext: boolean;\n /**\n * Set by AccessGate or PageDenyBoundary when a DenySignal is caught\n * server-side (inside the React tree, before React Flight sees it).\n * The pipeline reads this after render to set the HTTP status code.\n * See TIM-666.\n */\n denyStatus?: number;\n /**\n * Dev-only: getter for the current request's RSC debug components.\n * Set by renderRoute() so onPipelineError can include component tree\n * context for render-phase errors without module-level shared state.\n */\n debugComponentsGetter?: () => DebugComponentEntry[];\n}\n\n/** A single outgoing cookie entry in the cookie jar. */\nexport interface CookieEntry {\n name: string;\n value: string;\n options: import('./cookie-context.js').CookieOptions;\n}\n\n// ─── Tracing ──────────────────────────────────────────────────────────────\n// Used by: tracing.ts (getTraceId(), getSpanId())\n// Design doc: design/17-logging.md\n\nexport interface TraceStore {\n /** 32-char lowercase hex trace ID (OTEL or UUID fallback). */\n traceId: string;\n /** OTEL span ID if available, undefined otherwise. */\n spanId?: string;\n}\n\n/** @internal — import via tracing.ts public API */\nexport const traceAls = getOrCreateAls<TraceStore>(Symbol.for('timber:trace-als'));\n\n// ─── Server-Timing ────────────────────────────────────────────────────────\n// Used by: server-timing.ts (recordTiming(), withTiming())\n// Design doc: (dev-only performance instrumentation)\n\nexport interface TimingStore {\n entries: import('./server-timing.js').TimingEntry[];\n}\n\n/** @internal — import via server-timing.ts public API */\nexport const timingAls = getOrCreateAls<TimingStore>(Symbol.for('timber:timing-als'));\n\n// ─── Revalidation ─────────────────────────────────────────────────────────\n// Used by: actions.ts (revalidatePath(), revalidateTag())\n// Design doc: design/08-forms-and-actions.md\n\nexport interface RevalidationState {\n /** Paths to re-render (populated by revalidatePath calls). */\n paths: string[];\n /** Tags to invalidate (populated by revalidateTag calls). */\n tags: string[];\n}\n\n/** @internal — import via actions.ts public API */\nexport const revalidationAls = getOrCreateAls<RevalidationState>(\n Symbol.for('timber:revalidation-als')\n);\n\n// ─── Form Flash ───────────────────────────────────────────────────────────\n// Used by: form-flash.ts (getFormFlash())\n// Design doc: design/08-forms-and-actions.md §\"No-JS Error Round-Trip\"\n\n/** @internal — import via form-flash.ts public API */\nexport const formFlashAls = getOrCreateAls<import('./form-flash.js').FormFlashData>(\n Symbol.for('timber:form-flash-als')\n);\n\n// ─── Early Hints Sender ──────────────────────────────────────────────────\n// Used by: early-hints-sender.ts (sendEarlyHints103())\n// Design doc: design/02-rendering-pipeline.md §\"Early Hints (103)\"\n\n/** Function that sends Link header values as a 103 Early Hints response. */\nexport type EarlyHintsSenderFn = (links: string[]) => void;\n\n/** @internal — import via early-hints-sender.ts public API */\nexport const earlyHintsSenderAls = getOrCreateAls<EarlyHintsSenderFn>(\n Symbol.for('timber:early-hints-sender-als')\n);\n\n// ─── waitUntil Bridge ────────────────────────────────────────────────────\n// Used by: waituntil-bridge.ts (waitUntil())\n// Design doc: design/11-platform.md §\"waitUntil()\"\n\n/** Function that extends the request lifecycle with a background promise. */\nexport type WaitUntilFn = (promise: Promise<unknown>) => void;\n\n/** @internal — import via waituntil-bridge.ts public API */\nexport const waitUntilAls = getOrCreateAls<WaitUntilFn>(Symbol.for('timber:wait-until-als'));\n","/**\n * Tracing — per-request trace ID via AsyncLocalStorage, OTEL span helpers.\n *\n * getTraceId() is always available in server code (middleware, access, components, actions).\n * Returns a 32-char lowercase hex string — the OTEL trace ID when an SDK is active,\n * or a crypto.randomUUID()-derived fallback otherwise.\n *\n * See design/17-logging.md §\"trace_id is Always Set\"\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { traceAls, type TraceStore } from './als-registry.js';\n\n// Re-export the TraceStore type for public API consumers.\nexport type { TraceStore } from './als-registry.js';\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Returns the current request's trace ID — always a 32-char lowercase hex string.\n *\n * With OTEL: the real OTEL trace ID (matches Jaeger/Honeycomb/Datadog).\n * Without OTEL: crypto.randomUUID() with hyphens stripped.\n *\n * Throws if called outside a request context (no ALS store).\n */\nexport function getTraceId(): string {\n const store = traceAls.getStore();\n if (!store) {\n throw new Error(\n '[timber] getTraceId() called outside of a request context. ' +\n 'It can only be used in middleware, access checks, server components, and server actions.'\n );\n }\n return store.traceId;\n}\n\n/**\n * Returns the current OTEL span ID if available, undefined otherwise.\n */\nexport function getSpanId(): string | undefined {\n return traceAls.getStore()?.spanId;\n}\n\n// ─── Framework-Internal Helpers ───────────────────────────────────────────\n\n/**\n * Generate a 32-char lowercase hex ID from crypto.randomUUID().\n * Same format as OTEL trace IDs — zero-friction upgrade path.\n */\nexport function generateTraceId(): string {\n return randomUUID().replace(/-/g, '');\n}\n\n/**\n * Run a callback within a trace context. Used by the pipeline to establish\n * per-request ALS scope.\n */\nexport function runWithTraceId<T>(id: string, fn: () => T): T {\n return traceAls.run({ traceId: id }, fn);\n}\n\n/**\n * Replace the trace ID in the current ALS store. Used when OTEL creates\n * a root span and we want to switch from the UUID fallback to the real\n * OTEL trace ID.\n */\nexport function replaceTraceId(newTraceId: string, newSpanId?: string): void {\n const store = traceAls.getStore();\n if (store) {\n store.traceId = newTraceId;\n store.spanId = newSpanId;\n }\n}\n\n/**\n * Update the span ID in the current ALS store. Used when entering a new\n * OTEL span to keep log–trace correlation accurate.\n */\nexport function updateSpanId(newSpanId: string | undefined): void {\n const store = traceAls.getStore();\n if (store) {\n store.spanId = newSpanId;\n }\n}\n\n/**\n * Get the current trace store, or undefined if outside a request context.\n * Framework-internal — use getTraceId()/getSpanId() in user code.\n */\nexport function getTraceStore(): TraceStore | undefined {\n return traceAls.getStore();\n}\n\n// ─── Dev-Mode OTEL Auto-Init ─────────────────────────────────────────────\n\n/**\n * Well-known key marking dev tracing as initialized.\n *\n * The RSC entry module re-evaluates on every HMR invalidation and calls\n * initDevTracing() again — without this guard, each call would register a\n * fresh provider/processor and duplicate every span's output. Symbol.for()\n * survives module re-evaluation. See TIM-1067, B49.\n */\nconst DEV_TRACING_INIT_KEY = Symbol.for('timber.dev.tracing-initialized');\n\n/**\n * Initialize a minimal OTEL SDK in dev mode so spans are recorded and\n * fed to the DevSpanProcessor for dev log output.\n *\n * If the user already configured an OTEL SDK in register(), we add\n * our DevSpanProcessor alongside theirs. If no SDK is configured,\n * we create a BasicTracerProvider with our processor.\n *\n * Idempotent across module re-evaluations (HMR) — only the first call\n * registers. Only called in dev mode — zero overhead in production.\n */\nexport async function initDevTracing(\n config: import('../dev-tools/logger.js').DevLoggerConfig\n): Promise<void> {\n const globals = globalThis as Record<symbol, unknown>;\n if (globals[DEV_TRACING_INIT_KEY]) return;\n\n const api = await getOtelApi();\n if (!api) return;\n\n let DevSpanProcessor: typeof import('../dev-tools/instrumentation.js').DevSpanProcessor;\n let BasicTracerProvider: typeof import('@opentelemetry/sdk-trace-base').BasicTracerProvider;\n let AsyncLocalStorageContextManager: typeof import('@opentelemetry/context-async-hooks').AsyncLocalStorageContextManager;\n\n try {\n ({ DevSpanProcessor } = await import('../dev-tools/instrumentation.js'));\n ({ BasicTracerProvider } = await import('@opentelemetry/sdk-trace-base'));\n ({ AsyncLocalStorageContextManager } = await import('@opentelemetry/context-async-hooks'));\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[timber] Dev tracing disabled — failed to load OTEL packages:\\n ${msg}`);\n return;\n }\n\n const processor = new DevSpanProcessor(config);\n\n // Register a context manager so OTEL can propagate the active span\n // across async boundaries. Without this, startActiveSpan can't make\n // spans \"active\" — child spans get random trace IDs and getActiveSpan()\n // returns undefined.\n const contextManager = new AsyncLocalStorageContextManager();\n contextManager.enable();\n api.context.setGlobalContextManager(contextManager);\n\n // Create a minimal TracerProvider with our DevSpanProcessor.\n // If the user also configures an SDK in register(), their provider\n // will coexist — the global provider set last wins for new tracers,\n // but our processor captures all spans from the timber.js tracer.\n const provider = new BasicTracerProvider({\n spanProcessors: [processor],\n });\n api.trace.setGlobalTracerProvider(provider);\n\n // Reset cached tracer so next getTracer() picks up the new provider\n _tracer = undefined;\n\n globals[DEV_TRACING_INIT_KEY] = true;\n}\n\n// ─── OTEL Span Helpers ───────────────────────────────────────────────────\n\n/**\n * Attempt to get the @opentelemetry/api tracer. Returns undefined if the\n * package is not installed or no SDK is registered.\n *\n * timber.js depends on @opentelemetry/api as the vendor-neutral interface.\n * The API is a no-op by default — spans are only emitted when the developer\n * initializes an SDK in register().\n */\nlet _otelApi: typeof import('@opentelemetry/api') | null | undefined;\n\nasync function getOtelApi(): Promise<typeof import('@opentelemetry/api') | null> {\n if (_otelApi === undefined) {\n try {\n _otelApi = await import('@opentelemetry/api');\n } catch {\n _otelApi = null;\n }\n }\n return _otelApi;\n}\n\n/** OTEL tracer instance, lazily created. */\nlet _tracer: import('@opentelemetry/api').Tracer | null | undefined;\n\n/**\n * Get the timber.js OTEL tracer. Returns null if @opentelemetry/api is not available.\n */\nexport async function getTracer(): Promise<import('@opentelemetry/api').Tracer | null> {\n if (_tracer === undefined) {\n const api = await getOtelApi();\n if (api) {\n _tracer = api.trace.getTracer('timber.js');\n } else {\n _tracer = null;\n }\n }\n return _tracer;\n}\n\n/**\n * Run a function within an OTEL span. If OTEL is not available, runs the function\n * directly without any span overhead.\n *\n * Automatically:\n * - Creates the span as a child of the current context\n * - Updates the ALS span ID for log–trace correlation\n * - Ends the span when the function completes\n * - Records exceptions on error\n */\nexport async function withSpan<T>(\n name: string,\n attributes: Record<string, string | number | boolean>,\n fn: () => T | Promise<T>\n): Promise<T> {\n const tracer = await getTracer();\n if (!tracer) {\n return fn();\n }\n\n const api = (await getOtelApi())!;\n return tracer.startActiveSpan(name, { attributes }, async (span) => {\n const prevSpanId = getSpanId();\n updateSpanId(span.spanContext().spanId);\n try {\n const result = await fn();\n span.setStatus({ code: api.SpanStatusCode.OK });\n return result;\n } catch (error) {\n span.setStatus({ code: api.SpanStatusCode.ERROR });\n if (error instanceof Error) {\n span.recordException(error);\n }\n throw error;\n } finally {\n span.end();\n updateSpanId(prevSpanId);\n }\n });\n}\n\n/**\n * Set an attribute on the current active span (if any).\n * Used for setting span attributes after span creation (e.g. timber.result on access spans).\n */\nexport async function setSpanAttribute(\n key: string,\n value: string | number | boolean\n): Promise<void> {\n const api = await getOtelApi();\n if (!api) return;\n\n const activeSpan = api.trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.setAttribute(key, value);\n }\n}\n\n/**\n * Add a span event to the current active span (if any).\n * Used for timber.cache HIT/MISS events — recorded as span events, not child spans.\n */\nexport async function addSpanEvent(\n name: string,\n attributes?: Record<string, string | number | boolean>\n): Promise<void> {\n const api = await getOtelApi();\n if (!api) return;\n\n const activeSpan = api.trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.addEvent(name, attributes);\n }\n}\n\n/**\n * Fire-and-forget span event — no await, no microtask overhead.\n *\n * Used on the cache hot path where awaiting addSpanEvent creates an\n * unnecessary microtask per cache operation. If OTEL is not loaded yet,\n * the event is silently dropped (acceptable for diagnostics).\n *\n * See TIM-370 for perf motivation.\n */\nexport function addSpanEventSync(\n name: string,\n attributes?: Record<string, string | number | boolean>\n): void {\n // Fast path: if OTEL API hasn't been loaded yet, skip entirely.\n // _otelApi is undefined (not yet loaded), null (failed to load), or the module.\n if (!_otelApi) return;\n\n const activeSpan = _otelApi.trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.addEvent(name, attributes);\n }\n}\n\n/**\n * Try to extract the OTEL trace ID from the current active span context.\n * Returns undefined if OTEL is not active or no span exists.\n */\nexport async function getOtelTraceId(): Promise<{ traceId: string; spanId: string } | undefined> {\n const api = await getOtelApi();\n if (!api) return undefined;\n\n const activeSpan = api.trace.getActiveSpan();\n if (!activeSpan) return undefined;\n\n const ctx = activeSpan.spanContext();\n // OTEL uses \"0000000000000000\" as invalid trace IDs\n if (!ctx.traceId || ctx.traceId === '00000000000000000000000000000000') {\n return undefined;\n }\n\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n"],"mappings":"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"}