@timber-js/app 0.1.1 → 0.1.2

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 (141) hide show
  1. package/dist/index.js +5 -2
  2. package/dist/index.js.map +1 -1
  3. package/dist/plugins/entries.d.ts.map +1 -1
  4. package/package.json +2 -1
  5. package/src/adapters/cloudflare.ts +325 -0
  6. package/src/adapters/nitro.ts +366 -0
  7. package/src/adapters/types.ts +63 -0
  8. package/src/cache/index.ts +91 -0
  9. package/src/cache/redis-handler.ts +91 -0
  10. package/src/cache/register-cached-function.ts +99 -0
  11. package/src/cache/singleflight.ts +26 -0
  12. package/src/cache/stable-stringify.ts +21 -0
  13. package/src/cache/timber-cache.ts +116 -0
  14. package/src/cli.ts +201 -0
  15. package/src/client/browser-entry.ts +663 -0
  16. package/src/client/error-boundary.tsx +209 -0
  17. package/src/client/form.tsx +200 -0
  18. package/src/client/head.ts +61 -0
  19. package/src/client/history.ts +46 -0
  20. package/src/client/index.ts +60 -0
  21. package/src/client/link-navigate-interceptor.tsx +62 -0
  22. package/src/client/link-status-provider.tsx +40 -0
  23. package/src/client/link.tsx +310 -0
  24. package/src/client/nuqs-adapter.tsx +117 -0
  25. package/src/client/router-ref.ts +25 -0
  26. package/src/client/router.ts +563 -0
  27. package/src/client/segment-cache.ts +194 -0
  28. package/src/client/segment-context.ts +57 -0
  29. package/src/client/ssr-data.ts +95 -0
  30. package/src/client/types.ts +4 -0
  31. package/src/client/unload-guard.ts +34 -0
  32. package/src/client/use-cookie.ts +122 -0
  33. package/src/client/use-link-status.ts +46 -0
  34. package/src/client/use-navigation-pending.ts +47 -0
  35. package/src/client/use-params.ts +71 -0
  36. package/src/client/use-pathname.ts +43 -0
  37. package/src/client/use-query-states.ts +133 -0
  38. package/src/client/use-router.ts +77 -0
  39. package/src/client/use-search-params.ts +74 -0
  40. package/src/client/use-selected-layout-segment.ts +110 -0
  41. package/src/content/index.ts +13 -0
  42. package/src/cookies/define-cookie.ts +137 -0
  43. package/src/cookies/index.ts +9 -0
  44. package/src/fonts/ast.ts +359 -0
  45. package/src/fonts/css.ts +68 -0
  46. package/src/fonts/fallbacks.ts +248 -0
  47. package/src/fonts/google.ts +332 -0
  48. package/src/fonts/local.ts +177 -0
  49. package/src/fonts/types.ts +88 -0
  50. package/src/index.ts +413 -0
  51. package/src/plugins/adapter-build.ts +118 -0
  52. package/src/plugins/build-manifest.ts +323 -0
  53. package/src/plugins/build-report.ts +353 -0
  54. package/src/plugins/cache-transform.ts +199 -0
  55. package/src/plugins/chunks.ts +90 -0
  56. package/src/plugins/content.ts +136 -0
  57. package/src/plugins/dev-error-overlay.ts +230 -0
  58. package/src/plugins/dev-logs.ts +280 -0
  59. package/src/plugins/dev-server.ts +389 -0
  60. package/src/plugins/dynamic-transform.ts +161 -0
  61. package/src/plugins/entries.ts +207 -0
  62. package/src/plugins/fonts.ts +581 -0
  63. package/src/plugins/mdx.ts +179 -0
  64. package/src/plugins/react-prod.ts +56 -0
  65. package/src/plugins/routing.ts +419 -0
  66. package/src/plugins/server-action-exports.ts +220 -0
  67. package/src/plugins/server-bundle.ts +113 -0
  68. package/src/plugins/shims.ts +168 -0
  69. package/src/plugins/static-build.ts +207 -0
  70. package/src/routing/codegen.ts +396 -0
  71. package/src/routing/index.ts +14 -0
  72. package/src/routing/interception.ts +173 -0
  73. package/src/routing/scanner.ts +487 -0
  74. package/src/routing/status-file-lint.ts +114 -0
  75. package/src/routing/types.ts +100 -0
  76. package/src/search-params/analyze.ts +192 -0
  77. package/src/search-params/codecs.ts +153 -0
  78. package/src/search-params/create.ts +314 -0
  79. package/src/search-params/index.ts +23 -0
  80. package/src/search-params/registry.ts +31 -0
  81. package/src/server/access-gate.tsx +142 -0
  82. package/src/server/action-client.ts +473 -0
  83. package/src/server/action-handler.ts +325 -0
  84. package/src/server/actions.ts +236 -0
  85. package/src/server/asset-headers.ts +81 -0
  86. package/src/server/body-limits.ts +102 -0
  87. package/src/server/build-manifest.ts +234 -0
  88. package/src/server/canonicalize.ts +90 -0
  89. package/src/server/client-module-map.ts +58 -0
  90. package/src/server/csrf.ts +79 -0
  91. package/src/server/deny-renderer.ts +302 -0
  92. package/src/server/dev-logger.ts +419 -0
  93. package/src/server/dev-span-processor.ts +78 -0
  94. package/src/server/dev-warnings.ts +282 -0
  95. package/src/server/early-hints-sender.ts +55 -0
  96. package/src/server/early-hints.ts +142 -0
  97. package/src/server/error-boundary-wrapper.ts +69 -0
  98. package/src/server/error-formatter.ts +184 -0
  99. package/src/server/flush.ts +182 -0
  100. package/src/server/form-data.ts +176 -0
  101. package/src/server/form-flash.ts +93 -0
  102. package/src/server/html-injectors.ts +445 -0
  103. package/src/server/index.ts +222 -0
  104. package/src/server/instrumentation.ts +136 -0
  105. package/src/server/logger.ts +145 -0
  106. package/src/server/manifest-status-resolver.ts +215 -0
  107. package/src/server/metadata-render.ts +527 -0
  108. package/src/server/metadata-routes.ts +189 -0
  109. package/src/server/metadata.ts +263 -0
  110. package/src/server/middleware-runner.ts +32 -0
  111. package/src/server/nuqs-ssr-provider.tsx +63 -0
  112. package/src/server/pipeline.ts +555 -0
  113. package/src/server/prerender.ts +139 -0
  114. package/src/server/primitives.ts +264 -0
  115. package/src/server/proxy.ts +43 -0
  116. package/src/server/request-context.ts +554 -0
  117. package/src/server/route-element-builder.ts +395 -0
  118. package/src/server/route-handler.ts +153 -0
  119. package/src/server/route-matcher.ts +316 -0
  120. package/src/server/rsc-entry/api-handler.ts +112 -0
  121. package/src/server/rsc-entry/error-renderer.ts +177 -0
  122. package/src/server/rsc-entry/helpers.ts +147 -0
  123. package/src/server/rsc-entry/index.ts +688 -0
  124. package/src/server/rsc-entry/ssr-bridge.ts +18 -0
  125. package/src/server/slot-resolver.ts +359 -0
  126. package/src/server/ssr-entry.ts +161 -0
  127. package/src/server/ssr-render.ts +200 -0
  128. package/src/server/status-code-resolver.ts +282 -0
  129. package/src/server/tracing.ts +281 -0
  130. package/src/server/tree-builder.ts +354 -0
  131. package/src/server/types.ts +150 -0
  132. package/src/shims/font-google.ts +67 -0
  133. package/src/shims/headers.ts +11 -0
  134. package/src/shims/image.ts +48 -0
  135. package/src/shims/link.ts +9 -0
  136. package/src/shims/navigation-client.ts +52 -0
  137. package/src/shims/navigation.ts +31 -0
  138. package/src/shims/server-only-noop.js +5 -0
  139. package/src/utils/directive-parser.ts +529 -0
  140. package/src/utils/format.ts +10 -0
  141. package/src/utils/startup-timer.ts +102 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * RSC Entry Helpers — shared utilities for the RSC request handler.
3
+ *
4
+ * Small, stateless functions used across the RSC entry modules.
5
+ */
6
+
7
+ import type { ManifestSegmentNode } from '#/server/route-matcher.js';
8
+ import { RedirectSignal } from '#/server/primitives.js';
9
+
10
+ /** RSC content type for client navigation payload requests. */
11
+ export const RSC_CONTENT_TYPE = 'text/x-component';
12
+
13
+ /**
14
+ * Create a debug channel sink that discards all debug data.
15
+ *
16
+ * React Flight's dev mode serializes server component source code as `$E`
17
+ * entries for DevTools. Without a separate debugChannel, this data is
18
+ * embedded inline in the main RSC stream — leaking source code to the
19
+ * browser. By providing a debug channel, debug data goes to a separate
20
+ * stream that we drain and discard.
21
+ *
22
+ * See design/13-security.md §"Server component source leak"
23
+ *
24
+ * TODO: In the future, expose this debug data to the browser in dev mode
25
+ * for inline error overlays (e.g. component stack traces).
26
+ */
27
+ export function createDebugChannelSink(): { readable: ReadableStream; writable: WritableStream } {
28
+ const sink = new TransformStream();
29
+ // Drain the readable side so the writable never back-pressures.
30
+ sink.readable.pipeTo(new WritableStream()).catch(() => {});
31
+ return {
32
+ readable: new ReadableStream(), // no commands to send to Flight
33
+ writable: sink.writable,
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Build segment metadata for the X-Timber-Segments response header.
39
+ * Describes the rendered segment chain with async status, enabling
40
+ * the client to populate its segment cache for state tree diffing.
41
+ *
42
+ * Async detection: server components defined as `async function` have
43
+ * constructor.name === 'AsyncFunction'. These layouts always re-render
44
+ * on navigation (they may depend on request context like cookies/params).
45
+ * See design/07-routing.md §"Server Diffing Rules".
46
+ */
47
+ export function buildSegmentInfo(
48
+ segments: ManifestSegmentNode[],
49
+ layoutComponents: Array<{
50
+ component: (...args: unknown[]) => unknown;
51
+ segment: ManifestSegmentNode;
52
+ }>
53
+ ): Array<{ path: string; isAsync: boolean }> {
54
+ const layoutBySegment = new Map(
55
+ layoutComponents.map(({ component, segment }) => [segment, component])
56
+ );
57
+
58
+ // Deduplicate by path — route groups are transparent and share their
59
+ // parent's urlPath. When a group has its own layout, update the entry
60
+ // to reflect the group's async status (the layout is what matters for
61
+ // segment diffing). Without dedup, the state tree would contain
62
+ // duplicate paths that break the server's skip logic.
63
+ const byPath = new Map<string, { path: string; isAsync: boolean }>();
64
+
65
+ for (const segment of segments) {
66
+ const component = layoutBySegment.get(segment);
67
+ const isAsync = component?.constructor?.name === 'AsyncFunction';
68
+
69
+ const existing = byPath.get(segment.urlPath);
70
+ if (!existing) {
71
+ byPath.set(segment.urlPath, { path: segment.urlPath, isAsync });
72
+ } else if (component) {
73
+ // Group with a layout overrides the parent entry's async status
74
+ existing.isAsync = isAsync;
75
+ }
76
+ }
77
+
78
+ return Array.from(byPath.values());
79
+ }
80
+
81
+ /**
82
+ * Check if a request is asking for an RSC payload (client navigation)
83
+ * rather than full HTML. Client-side navigation sends Accept: text/x-component.
84
+ */
85
+ export function isRscPayloadRequest(req: Request): boolean {
86
+ const accept = req.headers.get('Accept') ?? '';
87
+ return accept.includes(RSC_CONTENT_TYPE);
88
+ }
89
+
90
+ /**
91
+ * Build a redirect Response. For RSC payload requests (client navigation),
92
+ * return 204 + X-Timber-Redirect header instead of a raw 302. The browser's
93
+ * fetch with redirect: "manual" turns a 302 into an opaque redirect (status 0,
94
+ * null body, inaccessible headers), which crashes createFromFetch when it
95
+ * tries to call .body.getReader(). The X-Timber-Redirect header lets the
96
+ * client detect the redirect and perform a soft SPA navigation.
97
+ */
98
+ export function buildRedirectResponse(
99
+ req: Request,
100
+ signal: RedirectSignal,
101
+ responseHeaders: Headers
102
+ ): Response {
103
+ if (isRscPayloadRequest(req)) {
104
+ responseHeaders.set('X-Timber-Redirect', signal.location);
105
+ return new Response(null, { status: 204, headers: responseHeaders });
106
+ }
107
+ responseHeaders.set('Location', signal.location);
108
+ return new Response(null, { status: signal.status, headers: responseHeaders });
109
+ }
110
+
111
+ /**
112
+ * Check if an error is an abort error (connection closed by client).
113
+ *
114
+ * When the browser aborts a request (page refresh, navigation away),
115
+ * the AbortSignal fires and React/streams throw an AbortError. This
116
+ * is not an application error — suppress it from error boundaries and logs.
117
+ */
118
+ export function isAbortError(error: unknown): boolean {
119
+ if (error instanceof DOMException && error.name === 'AbortError') return true;
120
+ if (error instanceof Error && error.name === 'AbortError') return true;
121
+ return false;
122
+ }
123
+
124
+ export function escapeHtml(str: string): string {
125
+ return str
126
+ .replace(/&/g, '&amp;')
127
+ .replace(/</g, '&lt;')
128
+ .replace(/>/g, '&gt;')
129
+ .replace(/"/g, '&quot;');
130
+ }
131
+
132
+ /**
133
+ * Parse a Cookie header string into a name→value Map.
134
+ * Used to populate NavContext.cookies for SSR hooks (useCookie).
135
+ */
136
+ export function parseCookiesFromHeader(header: string): Map<string, string> {
137
+ const map = new Map<string, string>();
138
+ if (!header) return map;
139
+ for (const pair of header.split(';')) {
140
+ const eqIndex = pair.indexOf('=');
141
+ if (eqIndex === -1) continue;
142
+ const name = pair.slice(0, eqIndex).trim();
143
+ const value = pair.slice(eqIndex + 1).trim();
144
+ if (name) map.set(name, value);
145
+ }
146
+ return map;
147
+ }