@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.
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/adapters/cloudflare.ts +325 -0
- package/src/adapters/nitro.ts +366 -0
- package/src/adapters/types.ts +63 -0
- package/src/cache/index.ts +91 -0
- package/src/cache/redis-handler.ts +91 -0
- package/src/cache/register-cached-function.ts +99 -0
- package/src/cache/singleflight.ts +26 -0
- package/src/cache/stable-stringify.ts +21 -0
- package/src/cache/timber-cache.ts +116 -0
- package/src/cli.ts +201 -0
- package/src/client/browser-entry.ts +663 -0
- package/src/client/error-boundary.tsx +209 -0
- package/src/client/form.tsx +200 -0
- package/src/client/head.ts +61 -0
- package/src/client/history.ts +46 -0
- package/src/client/index.ts +60 -0
- package/src/client/link-navigate-interceptor.tsx +62 -0
- package/src/client/link-status-provider.tsx +40 -0
- package/src/client/link.tsx +310 -0
- package/src/client/nuqs-adapter.tsx +117 -0
- package/src/client/router-ref.ts +25 -0
- package/src/client/router.ts +563 -0
- package/src/client/segment-cache.ts +194 -0
- package/src/client/segment-context.ts +57 -0
- package/src/client/ssr-data.ts +95 -0
- package/src/client/types.ts +4 -0
- package/src/client/unload-guard.ts +34 -0
- package/src/client/use-cookie.ts +122 -0
- package/src/client/use-link-status.ts +46 -0
- package/src/client/use-navigation-pending.ts +47 -0
- package/src/client/use-params.ts +71 -0
- package/src/client/use-pathname.ts +43 -0
- package/src/client/use-query-states.ts +133 -0
- package/src/client/use-router.ts +77 -0
- package/src/client/use-search-params.ts +74 -0
- package/src/client/use-selected-layout-segment.ts +110 -0
- package/src/content/index.ts +13 -0
- package/src/cookies/define-cookie.ts +137 -0
- package/src/cookies/index.ts +9 -0
- package/src/fonts/ast.ts +359 -0
- package/src/fonts/css.ts +68 -0
- package/src/fonts/fallbacks.ts +248 -0
- package/src/fonts/google.ts +332 -0
- package/src/fonts/local.ts +177 -0
- package/src/fonts/types.ts +88 -0
- package/src/index.ts +413 -0
- package/src/plugins/adapter-build.ts +118 -0
- package/src/plugins/build-manifest.ts +323 -0
- package/src/plugins/build-report.ts +353 -0
- package/src/plugins/cache-transform.ts +199 -0
- package/src/plugins/chunks.ts +90 -0
- package/src/plugins/content.ts +136 -0
- package/src/plugins/dev-error-overlay.ts +230 -0
- package/src/plugins/dev-logs.ts +280 -0
- package/src/plugins/dev-server.ts +389 -0
- package/src/plugins/dynamic-transform.ts +161 -0
- package/src/plugins/entries.ts +207 -0
- package/src/plugins/fonts.ts +581 -0
- package/src/plugins/mdx.ts +179 -0
- package/src/plugins/react-prod.ts +56 -0
- package/src/plugins/routing.ts +419 -0
- package/src/plugins/server-action-exports.ts +220 -0
- package/src/plugins/server-bundle.ts +113 -0
- package/src/plugins/shims.ts +168 -0
- package/src/plugins/static-build.ts +207 -0
- package/src/routing/codegen.ts +396 -0
- package/src/routing/index.ts +14 -0
- package/src/routing/interception.ts +173 -0
- package/src/routing/scanner.ts +487 -0
- package/src/routing/status-file-lint.ts +114 -0
- package/src/routing/types.ts +100 -0
- package/src/search-params/analyze.ts +192 -0
- package/src/search-params/codecs.ts +153 -0
- package/src/search-params/create.ts +314 -0
- package/src/search-params/index.ts +23 -0
- package/src/search-params/registry.ts +31 -0
- package/src/server/access-gate.tsx +142 -0
- package/src/server/action-client.ts +473 -0
- package/src/server/action-handler.ts +325 -0
- package/src/server/actions.ts +236 -0
- package/src/server/asset-headers.ts +81 -0
- package/src/server/body-limits.ts +102 -0
- package/src/server/build-manifest.ts +234 -0
- package/src/server/canonicalize.ts +90 -0
- package/src/server/client-module-map.ts +58 -0
- package/src/server/csrf.ts +79 -0
- package/src/server/deny-renderer.ts +302 -0
- package/src/server/dev-logger.ts +419 -0
- package/src/server/dev-span-processor.ts +78 -0
- package/src/server/dev-warnings.ts +282 -0
- package/src/server/early-hints-sender.ts +55 -0
- package/src/server/early-hints.ts +142 -0
- package/src/server/error-boundary-wrapper.ts +69 -0
- package/src/server/error-formatter.ts +184 -0
- package/src/server/flush.ts +182 -0
- package/src/server/form-data.ts +176 -0
- package/src/server/form-flash.ts +93 -0
- package/src/server/html-injectors.ts +445 -0
- package/src/server/index.ts +222 -0
- package/src/server/instrumentation.ts +136 -0
- package/src/server/logger.ts +145 -0
- package/src/server/manifest-status-resolver.ts +215 -0
- package/src/server/metadata-render.ts +527 -0
- package/src/server/metadata-routes.ts +189 -0
- package/src/server/metadata.ts +263 -0
- package/src/server/middleware-runner.ts +32 -0
- package/src/server/nuqs-ssr-provider.tsx +63 -0
- package/src/server/pipeline.ts +555 -0
- package/src/server/prerender.ts +139 -0
- package/src/server/primitives.ts +264 -0
- package/src/server/proxy.ts +43 -0
- package/src/server/request-context.ts +554 -0
- package/src/server/route-element-builder.ts +395 -0
- package/src/server/route-handler.ts +153 -0
- package/src/server/route-matcher.ts +316 -0
- package/src/server/rsc-entry/api-handler.ts +112 -0
- package/src/server/rsc-entry/error-renderer.ts +177 -0
- package/src/server/rsc-entry/helpers.ts +147 -0
- package/src/server/rsc-entry/index.ts +688 -0
- package/src/server/rsc-entry/ssr-bridge.ts +18 -0
- package/src/server/slot-resolver.ts +359 -0
- package/src/server/ssr-entry.ts +161 -0
- package/src/server/ssr-render.ts +200 -0
- package/src/server/status-code-resolver.ts +282 -0
- package/src/server/tracing.ts +281 -0
- package/src/server/tree-builder.ts +354 -0
- package/src/server/types.ts +150 -0
- package/src/shims/font-google.ts +67 -0
- package/src/shims/headers.ts +11 -0
- package/src/shims/image.ts +48 -0
- package/src/shims/link.ts +9 -0
- package/src/shims/navigation-client.ts +52 -0
- package/src/shims/navigation.ts +31 -0
- package/src/shims/server-only-noop.js +5 -0
- package/src/utils/directive-parser.ts +529 -0
- package/src/utils/format.ts +10 -0
- 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, '&')
|
|
127
|
+
.replace(/</g, '<')
|
|
128
|
+
.replace(/>/g, '>')
|
|
129
|
+
.replace(/"/g, '"');
|
|
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
|
+
}
|