@timber-js/app 0.2.0-alpha.71 → 0.2.0-alpha.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/actions-Dg-ANYHb.js +421 -0
- package/dist/_chunks/actions-Dg-ANYHb.js.map +1 -0
- package/dist/_chunks/{als-registry-BJARkOcu.js → als-registry-HS0LGUl2.js} +1 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +1 -0
- package/dist/_chunks/{define-Dz1bqwaS.js → define-C77ScO0m.js} +14 -14
- package/dist/_chunks/define-C77ScO0m.js.map +1 -0
- package/dist/_chunks/{define-CGuYoRHU.js → define-CZqDwhSu.js} +15 -15
- package/dist/_chunks/define-CZqDwhSu.js.map +1 -0
- package/dist/_chunks/{define-cookie-B5mewxwM.js → define-cookie-C2IkoFGN.js} +9 -8
- package/dist/_chunks/{define-cookie-B5mewxwM.js.map → define-cookie-C2IkoFGN.js.map} +1 -1
- package/dist/_chunks/{format-Rn922VH2.js → dev-warnings-DpGRGoDi.js} +4 -26
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +1 -0
- package/dist/_chunks/format-CYBGxKtc.js +14 -0
- package/dist/_chunks/format-CYBGxKtc.js.map +1 -0
- package/dist/_chunks/{interception-CEdHHviP.js → interception-Dpn_UfAD.js} +2 -2
- package/dist/_chunks/{interception-CEdHHviP.js.map → interception-Dpn_UfAD.js.map} +1 -1
- package/dist/_chunks/{segment-context-hzuJ048X.js → merge-search-params-Cm_KIWDX.js} +2 -33
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
- package/dist/_chunks/{request-context-CywiO4jV.js → request-context-qMsWgy9C.js} +72 -36
- package/dist/_chunks/request-context-qMsWgy9C.js.map +1 -0
- package/dist/_chunks/{schema-bridge-C4SwjCQD.js → schema-bridge-C3xl_vfb.js} +1 -1
- package/dist/_chunks/{schema-bridge-C4SwjCQD.js.map → schema-bridge-C3xl_vfb.js.map} +1 -1
- package/dist/_chunks/segment-context-fHFLF1PE.js +34 -0
- package/dist/_chunks/segment-context-fHFLF1PE.js.map +1 -0
- package/dist/_chunks/ssr-data-DzuI0bIV.js +88 -0
- package/dist/_chunks/ssr-data-DzuI0bIV.js.map +1 -0
- package/dist/_chunks/{stale-reload-BLUC_Pl_.js → stale-reload-C2plcNtG.js} +1 -1
- package/dist/_chunks/{stale-reload-BLUC_Pl_.js.map → stale-reload-C2plcNtG.js.map} +1 -1
- package/dist/_chunks/{handler-store-BVePM7hp.js → tracing-CCYbKn5n.js} +60 -60
- package/dist/_chunks/tracing-CCYbKn5n.js.map +1 -0
- package/dist/_chunks/use-params-B1AuhI1p.js +307 -0
- package/dist/_chunks/use-params-B1AuhI1p.js.map +1 -0
- package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-Lo_s_pw2.js} +4 -4
- package/dist/_chunks/use-query-states-Lo_s_pw2.js.map +1 -0
- package/dist/_chunks/{wrappers-LZbghvn0.js → wrappers-_DTmImGt.js} +1 -1
- package/dist/_chunks/{wrappers-LZbghvn0.js.map → wrappers-_DTmImGt.js.map} +1 -1
- package/dist/adapters/cloudflare-kv-cache.d.ts +64 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.js +95 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/cache/index.d.ts +18 -4
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +78 -12
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/sizeof.d.ts +22 -0
- package/dist/cache/sizeof.d.ts.map +1 -0
- package/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +27 -1
- package/dist/client/browser-dev.d.ts.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
- package/dist/client/browser-entry/hmr.d.ts +21 -0
- package/dist/client/browser-entry/hmr.d.ts.map +1 -0
- package/dist/client/browser-entry/hydrate.d.ts +46 -0
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
- package/dist/client/browser-entry/index.d.ts +30 -0
- package/dist/client/browser-entry/index.d.ts.map +1 -0
- package/dist/client/browser-entry/post-hydration.d.ts +26 -0
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts +23 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -0
- package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
- package/dist/client/browser-entry/scroll.d.ts +19 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- package/dist/client/error-boundary.js +131 -1
- package/dist/client/error-boundary.js.map +1 -0
- package/dist/client/index.d.ts +4 -19
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +14 -1191
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +18 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +890 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/top-loader.d.ts +2 -2
- package/dist/client/use-link-status.d.ts +1 -1
- package/dist/client/{use-navigation-pending.d.ts → use-pending-navigation.d.ts} +4 -4
- package/dist/client/use-pending-navigation.d.ts.map +1 -0
- package/dist/client/use-router.d.ts +1 -1
- package/dist/codec.d.ts +10 -0
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +1 -1
- package/dist/config-types.d.ts +210 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/content/index.d.ts +1 -10
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +0 -2
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +0 -2
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +2 -3
- package/dist/index.d.ts +25 -288
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +261 -43
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +84 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +1 -1
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +1 -1
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-browser-logs.d.ts +1 -1
- package/dist/plugins/dev-browser-logs.d.ts.map +1 -1
- package/dist/plugins/dev-logs.d.ts +1 -1
- package/dist/plugins/dev-logs.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +1 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +4 -4
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/search-params/define.d.ts +6 -6
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.d.ts +1 -2
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +4 -4
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/registry.d.ts.map +1 -1
- package/dist/segment-params/define.d.ts +6 -6
- package/dist/segment-params/define.d.ts.map +1 -1
- package/dist/segment-params/index.d.ts +0 -1
- package/dist/segment-params/index.d.ts.map +1 -1
- package/dist/segment-params/index.js +3 -3
- package/dist/server/als-registry.d.ts +1 -1
- package/dist/server/dev-holding-server.d.ts +52 -0
- package/dist/server/dev-holding-server.d.ts.map +1 -0
- package/dist/server/dev-warnings.d.ts +1 -7
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/index.d.ts +6 -45
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +7 -3272
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +46 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +2865 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +41 -17
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +45 -15
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +4 -4
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/shims/headers.d.ts +2 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +2 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/package.json +19 -13
- package/src/adapters/cloudflare-kv-cache.ts +142 -0
- package/src/cache/handler-store.ts +2 -2
- package/src/cache/index.ts +74 -15
- package/src/cache/sizeof.ts +31 -0
- package/src/cli.ts +6 -1
- package/src/client/browser-dev.ts +128 -1
- package/src/client/browser-entry/action-dispatch.ts +116 -0
- package/src/client/browser-entry/hmr.ts +81 -0
- package/src/client/browser-entry/hydrate.ts +145 -0
- package/src/client/browser-entry/index.ts +138 -0
- package/src/client/browser-entry/post-hydration.ts +119 -0
- package/src/client/browser-entry/router-init.ts +184 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +27 -0
- package/src/client/index.ts +10 -38
- package/src/client/internal.ts +57 -0
- package/src/client/navigation-context.ts +6 -2
- package/src/client/navigation-root.tsx +1 -1
- package/src/client/router-ref.ts +1 -1
- package/src/client/top-loader.tsx +2 -2
- package/src/client/use-link-status.ts +1 -1
- package/src/client/{use-navigation-pending.ts → use-pending-navigation.ts} +5 -5
- package/src/client/use-query-states.ts +2 -2
- package/src/client/use-router.ts +1 -1
- package/src/codec.ts +15 -0
- package/src/config-types.ts +208 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +9 -7
- package/src/cookies/index.ts +6 -5
- package/src/index.ts +84 -416
- package/src/plugin-context.ts +200 -0
- package/src/plugins/adapter-build.ts +1 -1
- package/src/plugins/build-manifest.ts +1 -1
- package/src/plugins/build-report.ts +1 -1
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +1 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +16 -1
- package/src/plugins/entries.ts +2 -2
- package/src/plugins/fonts.ts +4 -3
- package/src/plugins/mdx.ts +1 -1
- package/src/plugins/routing.ts +1 -1
- package/src/plugins/shims.ts +53 -5
- package/src/plugins/static-build.ts +8 -6
- package/src/search-params/define.ts +22 -22
- package/src/search-params/index.ts +3 -3
- package/src/search-params/registry.ts +1 -1
- package/src/segment-params/define.ts +18 -18
- package/src/segment-params/index.ts +2 -1
- package/src/server/action-handler.ts +1 -1
- package/src/server/als-registry.ts +3 -3
- package/src/server/dev-holding-server.ts +185 -0
- package/src/server/dev-warnings.ts +2 -21
- package/src/server/html-injectors.ts +3 -3
- package/src/server/index.ts +25 -180
- package/src/server/internal.ts +169 -0
- package/src/server/pipeline.ts +12 -7
- package/src/server/primitives.ts +71 -30
- package/src/server/request-context.ts +77 -39
- package/src/server/route-element-builder.ts +1 -1
- package/src/server/rsc-entry/index.ts +2 -2
- package/src/server/rsc-entry/ssr-renderer.ts +1 -1
- package/src/server/slot-resolver.ts +1 -1
- package/src/server/tracing.ts +6 -6
- package/src/server/tree-builder.ts +1 -1
- package/src/shims/headers.ts +5 -1
- package/src/shims/navigation.ts +5 -1
- package/dist/_chunks/als-registry-BJARkOcu.js.map +0 -1
- package/dist/_chunks/define-CGuYoRHU.js.map +0 -1
- package/dist/_chunks/define-Dz1bqwaS.js.map +0 -1
- package/dist/_chunks/error-boundary-D9hzsveV.js +0 -216
- package/dist/_chunks/error-boundary-D9hzsveV.js.map +0 -1
- package/dist/_chunks/format-Rn922VH2.js.map +0 -1
- package/dist/_chunks/handler-store-BVePM7hp.js.map +0 -1
- package/dist/_chunks/request-context-CywiO4jV.js.map +0 -1
- package/dist/_chunks/segment-context-hzuJ048X.js.map +0 -1
- package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +0 -1
- package/dist/client/browser-entry.d.ts +0 -21
- package/dist/client/browser-entry.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts.map +0 -1
- package/src/client/browser-entry.ts +0 -846
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin context — internal types and helpers for timber sub-plugins.
|
|
3
|
+
*
|
|
4
|
+
* These are NOT part of the user-facing API. They are consumed by
|
|
5
|
+
* sub-plugins (routing, entries, shims, etc.) via relative imports.
|
|
6
|
+
* The root entry point (index.ts) does not re-export them.
|
|
7
|
+
*
|
|
8
|
+
* Design doc: 18-build-system.md §"Shared Plugin Context"
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import type { RouteTree } from './routing/types';
|
|
15
|
+
import type { BuildManifest } from './server/build-manifest';
|
|
16
|
+
import type { StartupTimer } from './utils/startup-timer';
|
|
17
|
+
import { createStartupTimer } from './utils/startup-timer';
|
|
18
|
+
import type { TimberUserConfig, ClientJavascriptConfig } from './config-types.js';
|
|
19
|
+
import type { HoldingServer } from './server/dev-holding-server.js';
|
|
20
|
+
|
|
21
|
+
// Re-export for sub-plugin convenience — they import from plugin-context.ts
|
|
22
|
+
export type { TimberUserConfig, ClientJavascriptConfig } from './config-types.js';
|
|
23
|
+
|
|
24
|
+
// ── Client JavaScript config ─────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/** Fully resolved client JavaScript configuration (no optionals). */
|
|
27
|
+
export interface ResolvedClientJavascript {
|
|
28
|
+
disabled: boolean;
|
|
29
|
+
enableHMRInDev: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve `clientJavascript` into a fully resolved config.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveClientJavascript(config: TimberUserConfig): ResolvedClientJavascript {
|
|
36
|
+
if (config.clientJavascript !== undefined) {
|
|
37
|
+
if (typeof config.clientJavascript === 'boolean') {
|
|
38
|
+
return {
|
|
39
|
+
disabled: !config.clientJavascript,
|
|
40
|
+
enableHMRInDev: !config.clientJavascript,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
disabled: config.clientJavascript.disabled,
|
|
45
|
+
enableHMRInDev: config.clientJavascript.enableHMRInDev ?? config.clientJavascript.disabled,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { disabled: false, enableHMRInDev: false };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Plugin Context ────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Shared context object passed to all sub-plugins via closure.
|
|
55
|
+
*
|
|
56
|
+
* Sub-plugins communicate through this context — not through Vite's
|
|
57
|
+
* plugin API or global state.
|
|
58
|
+
* See design/18-build-system.md §"Shared Plugin Context".
|
|
59
|
+
*/
|
|
60
|
+
export interface PluginContext {
|
|
61
|
+
config: TimberUserConfig;
|
|
62
|
+
/** Resolved client JavaScript configuration */
|
|
63
|
+
clientJavascript: ResolvedClientJavascript;
|
|
64
|
+
/** The scanned route tree (populated by timber-routing, consumed by timber-entries) */
|
|
65
|
+
routeTree: RouteTree | null;
|
|
66
|
+
/** Absolute path to the app/ directory */
|
|
67
|
+
appDir: string;
|
|
68
|
+
/** Absolute path to the project root */
|
|
69
|
+
root: string;
|
|
70
|
+
/** Whether the dev server is running (set by timber-root-sync in configResolved) */
|
|
71
|
+
dev: boolean;
|
|
72
|
+
/** CSS build manifest (populated by adapter after client build, null in dev) */
|
|
73
|
+
buildManifest: BuildManifest | null;
|
|
74
|
+
/** Per-build deployment ID for version skew detection (null in dev) */
|
|
75
|
+
deploymentId: string | null;
|
|
76
|
+
/** Startup timer for profiling cold start phases (active in dev, no-op in prod) */
|
|
77
|
+
timer: StartupTimer;
|
|
78
|
+
/** Holding server that binds the port during dev startup (closed in timber-dev-server) */
|
|
79
|
+
holdingServer?: HoldingServer | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── App directory resolution ──────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the app directory. Checks (in order):
|
|
86
|
+
* 1. Explicit `configAppDir` from timber.config.ts
|
|
87
|
+
* 2. `<root>/app`
|
|
88
|
+
* 3. `<root>/src/app`
|
|
89
|
+
*
|
|
90
|
+
* Throws if none exist.
|
|
91
|
+
*/
|
|
92
|
+
export function resolveAppDir(root: string, configAppDir?: string): string {
|
|
93
|
+
if (configAppDir) {
|
|
94
|
+
const explicit = join(root, configAppDir);
|
|
95
|
+
if (!existsSync(explicit)) {
|
|
96
|
+
throw new Error(`[timber] Configured appDir "${configAppDir}" does not exist at ${explicit}`);
|
|
97
|
+
}
|
|
98
|
+
return explicit;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const rootApp = join(root, 'app');
|
|
102
|
+
if (existsSync(rootApp)) return rootApp;
|
|
103
|
+
|
|
104
|
+
const srcApp = join(root, 'src', 'app');
|
|
105
|
+
if (existsSync(srcApp)) return srcApp;
|
|
106
|
+
|
|
107
|
+
throw new Error(
|
|
108
|
+
`[timber] Could not find app directory. Expected "app/" or "src/app/" in ${root}. ` +
|
|
109
|
+
`You can set appDir in timber.config.ts to specify a custom location.`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Plugin context factory ────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
export function createPluginContext(config?: TimberUserConfig, root?: string): PluginContext {
|
|
116
|
+
const projectRoot = root ?? process.cwd();
|
|
117
|
+
const resolvedConfig: TimberUserConfig = { ...config };
|
|
118
|
+
return {
|
|
119
|
+
config: resolvedConfig,
|
|
120
|
+
clientJavascript: resolveClientJavascript(resolvedConfig),
|
|
121
|
+
routeTree: null,
|
|
122
|
+
appDir: join(projectRoot, 'app'),
|
|
123
|
+
root: projectRoot,
|
|
124
|
+
dev: false,
|
|
125
|
+
buildManifest: null,
|
|
126
|
+
deploymentId: null,
|
|
127
|
+
timer: createStartupTimer(),
|
|
128
|
+
holdingServer: null,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── Config file loading ───────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Load timber.config.ts (or .js, .mjs) from the project root.
|
|
136
|
+
* Returns the config object or null if no config file is found.
|
|
137
|
+
*
|
|
138
|
+
* Uses require() which works for ESM modules on Node 22.12+.
|
|
139
|
+
* This keeps timber() synchronous — no async config loading needed.
|
|
140
|
+
*/
|
|
141
|
+
export function loadTimberConfigFile(root: string): TimberUserConfig | null {
|
|
142
|
+
const configNames = ['timber.config.ts', 'timber.config.js', 'timber.config.mjs'];
|
|
143
|
+
const req = createRequire(join(root, 'package.json'));
|
|
144
|
+
|
|
145
|
+
for (const name of configNames) {
|
|
146
|
+
const configPath = join(root, name);
|
|
147
|
+
if (existsSync(configPath)) {
|
|
148
|
+
const mod = req(configPath);
|
|
149
|
+
return (mod.default ?? mod) as TimberUserConfig;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Detect config keys set in both inline (vite.config.ts) and file (timber.config.ts)
|
|
157
|
+
* and warn the user. The `output` key is excluded because it defaults to 'server'
|
|
158
|
+
* in createPluginContext and would always appear as an inline key.
|
|
159
|
+
*
|
|
160
|
+
* Returns the list of conflicting key names (for testing).
|
|
161
|
+
*/
|
|
162
|
+
export function warnConfigConflicts(
|
|
163
|
+
inline: TimberUserConfig,
|
|
164
|
+
fileConfig: TimberUserConfig
|
|
165
|
+
): string[] {
|
|
166
|
+
const conflicts: string[] = [];
|
|
167
|
+
for (const key of Object.keys(fileConfig) as (keyof TimberUserConfig)[]) {
|
|
168
|
+
if (key in inline && inline[key] !== undefined) {
|
|
169
|
+
conflicts.push(key);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (conflicts.length > 0) {
|
|
173
|
+
console.warn(
|
|
174
|
+
`[timber] Config conflict: ${conflicts.map((k) => `"${k}"`).join(', ')} set in both ` +
|
|
175
|
+
`vite.config.ts (inline) and timber.config.ts. ` +
|
|
176
|
+
`Move all config to timber.config.ts to avoid confusion. ` +
|
|
177
|
+
`The inline value from vite.config.ts will be used.`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return conflicts;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Merge file-based config into ctx.config. Inline config (already in ctx.config)
|
|
185
|
+
* takes precedence — file config only fills in missing fields.
|
|
186
|
+
*/
|
|
187
|
+
export function mergeFileConfig(ctx: PluginContext, fileConfig: TimberUserConfig): void {
|
|
188
|
+
const inline = ctx.config;
|
|
189
|
+
warnConfigConflicts(inline, fileConfig);
|
|
190
|
+
|
|
191
|
+
ctx.config = {
|
|
192
|
+
...fileConfig,
|
|
193
|
+
...inline,
|
|
194
|
+
...(fileConfig.limits && inline.limits
|
|
195
|
+
? { limits: { ...fileConfig.limits, ...inline.limits } }
|
|
196
|
+
: {}),
|
|
197
|
+
...(fileConfig.dev && inline.dev ? { dev: { ...fileConfig.dev, ...inline.dev } } : {}),
|
|
198
|
+
...(fileConfig.mdx && inline.mdx ? { mdx: { ...fileConfig.mdx, ...inline.mdx } } : {}),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import type { Plugin } from 'vite';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
18
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
19
|
-
import type { PluginContext } from '../
|
|
19
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
20
20
|
import type { TimberPlatformAdapter, TimberConfig } from '../adapters/types.js';
|
|
21
21
|
|
|
22
22
|
export function timberAdapterBuild(ctx: PluginContext): Plugin {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
20
|
import type { Plugin, ResolvedConfig } from 'vite';
|
|
21
|
-
import type { PluginContext } from '../
|
|
21
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
22
22
|
import type { BuildManifest } from '../server/build-manifest.js';
|
|
23
23
|
|
|
24
24
|
// Rollup types used by generateBundle hook — imported from vite which re-exports them.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { gzipSync } from 'node:zlib';
|
|
17
17
|
import type { Plugin, Logger } from 'vite';
|
|
18
|
-
import type { PluginContext } from '../
|
|
18
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
19
19
|
import type { SegmentNode, RouteTree } from '../routing/types.js';
|
|
20
20
|
import { formatSize } from '../utils/format.js';
|
|
21
21
|
|
package/src/plugins/content.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import type { Plugin } from 'vite';
|
|
12
12
|
import { existsSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import type { PluginContext } from '../
|
|
14
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
15
15
|
|
|
16
16
|
const CONFIG_FILE_NAMES = [
|
|
17
17
|
'content-collections.ts',
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { Plugin, ViteDevServer } from 'vite';
|
|
19
|
-
import type { PluginContext } from '../
|
|
19
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
20
20
|
|
|
21
21
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
22
22
|
|
package/src/plugins/dev-logs.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { Plugin, ViteDevServer } from 'vite';
|
|
16
|
-
import type { PluginContext } from '../
|
|
16
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
17
17
|
|
|
18
18
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import type { Plugin, ViteDevServer, DevEnvironment } from 'vite';
|
|
16
16
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
-
import type { PluginContext } from '../
|
|
18
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
19
19
|
import { setViteServer } from '../server/dev-warnings.js';
|
|
20
20
|
import { sendErrorToOverlay, classifyErrorPhase, parseFirstAppFrame } from './dev-error-overlay.js';
|
|
21
21
|
import { compressResponse } from '../server/compress.js';
|
|
@@ -96,6 +96,21 @@ export function timberDevServer(ctx: PluginContext): Plugin {
|
|
|
96
96
|
// Pre-hook — registers middleware before Vite's internals
|
|
97
97
|
server.middlewares.use(createTimberMiddleware(server, ctx.root));
|
|
98
98
|
|
|
99
|
+
// Wrap server.listen() to close the holding server immediately
|
|
100
|
+
// before Vite binds the port — no gap where nothing is listening.
|
|
101
|
+
// The holding server was started in rootSync's config() hook to
|
|
102
|
+
// serve a loading page during initialization.
|
|
103
|
+
// See design/21-dev-server.md §"Startup Holding Server", TIM-665.
|
|
104
|
+
if (ctx.holdingServer) {
|
|
105
|
+
const originalListen = server.listen.bind(server);
|
|
106
|
+
const holdingRef = ctx.holdingServer;
|
|
107
|
+
ctx.holdingServer = null;
|
|
108
|
+
server.listen = async (port?: number, isRestart?: boolean) => {
|
|
109
|
+
await holdingRef.close().catch(() => {});
|
|
110
|
+
return originalListen(port, isRestart);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
99
114
|
// Log startup timing summary. configureServer runs on all plugins
|
|
100
115
|
// before the server listens, so this captures the full cold start.
|
|
101
116
|
ctx.timer.end('dev-server-setup');
|
package/src/plugins/entries.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { Plugin } from 'vite';
|
|
|
15
15
|
import { resolve, dirname } from 'node:path';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { existsSync } from 'node:fs';
|
|
18
|
-
import type { PluginContext } from '../
|
|
18
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
19
19
|
|
|
20
20
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
21
|
|
|
@@ -49,7 +49,7 @@ const VIRTUAL_IDS = {
|
|
|
49
49
|
const ENTRY_FILE_MAP: Record<string, string> = {
|
|
50
50
|
[VIRTUAL_IDS.rscEntry]: resolve(SRC_DIR, 'server', 'rsc-entry', 'index.ts'),
|
|
51
51
|
[VIRTUAL_IDS.ssrEntry]: resolve(SRC_DIR, 'server', 'ssr-entry.ts'),
|
|
52
|
-
[VIRTUAL_IDS.browserEntry]: resolve(SRC_DIR, 'client', 'browser-entry.ts'),
|
|
52
|
+
[VIRTUAL_IDS.browserEntry]: resolve(SRC_DIR, 'client', 'browser-entry', 'index.ts'),
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
/** The \0-prefixed resolved ID for virtual:timber-config */
|
package/src/plugins/fonts.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import type { Plugin, ViteDevServer } from 'vite';
|
|
18
18
|
import { readFileSync, existsSync } from 'node:fs';
|
|
19
19
|
import { resolve, normalize } from 'node:path';
|
|
20
|
-
import type { PluginContext } from '../
|
|
20
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
21
21
|
import type { ExtractedFont, GoogleFontConfig } from '../fonts/types.js';
|
|
22
22
|
import type { ManifestFontEntry } from '../server/build-manifest.js';
|
|
23
23
|
import { generateVariableClass, generateFontFamilyClass, generateFontFaces } from '../fonts/css.js';
|
|
@@ -470,11 +470,12 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
470
470
|
googleFontFacesMap.set(font.id, faces);
|
|
471
471
|
} catch (e) {
|
|
472
472
|
// In dev mode, fail gracefully — fonts fall back to system fonts.
|
|
473
|
+
// Don't cache the failure so transient errors (network blips,
|
|
474
|
+
// rate limits) are retried on the next request. See TIM-636.
|
|
473
475
|
const msg = e instanceof Error ? e.message : String(e);
|
|
474
476
|
console.warn(
|
|
475
|
-
`[timber-fonts] Failed to resolve Google font "${font.family}": ${msg}
|
|
477
|
+
`[timber-fonts] Failed to resolve Google font "${font.family}": ${msg}. Will retry on next request.`
|
|
476
478
|
);
|
|
477
|
-
googleFontFacesMap.set(font.id, []);
|
|
478
479
|
}
|
|
479
480
|
}
|
|
480
481
|
}
|
package/src/plugins/mdx.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import type { Plugin } from 'vite';
|
|
12
12
|
import { existsSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import type { PluginContext } from '../
|
|
14
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
15
15
|
|
|
16
16
|
const MDX_EXTENSIONS = ['mdx', 'md'];
|
|
17
17
|
|
package/src/plugins/routing.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
formatStatusFileLintWarnings,
|
|
20
20
|
} from '../routing/status-file-lint.js';
|
|
21
21
|
import type { RouteTree, SegmentNode, RouteFile } from '../routing/types.js';
|
|
22
|
-
import type { PluginContext } from '../
|
|
22
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
23
23
|
|
|
24
24
|
const VIRTUAL_MODULE_ID = 'virtual:timber-route-manifest';
|
|
25
25
|
const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_MODULE_ID}`;
|
package/src/plugins/shims.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import type { Plugin } from 'vite';
|
|
18
18
|
import { resolve, dirname } from 'node:path';
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
20
|
-
import type { PluginContext } from '../
|
|
20
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
21
21
|
|
|
22
22
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
23
|
// Detect whether we're running from source (src/plugins/) or dist (dist/).
|
|
@@ -48,11 +48,9 @@ const CLIENT_ONLY_VIRTUAL = '\0timber:client-only';
|
|
|
48
48
|
*/
|
|
49
49
|
const SUBPATH_SRC_MAP: Record<string, string> = {
|
|
50
50
|
'cache': 'cache/index.ts',
|
|
51
|
-
'content': 'content/index.ts',
|
|
52
51
|
'cookies': 'cookies/index.ts',
|
|
53
|
-
'segment-params': 'segment-params/index.ts',
|
|
54
52
|
'search-params': 'search-params/index.ts',
|
|
55
|
-
|
|
53
|
+
|
|
56
54
|
'adapters/cloudflare': 'adapters/cloudflare.ts',
|
|
57
55
|
'adapters/cloudflare/dev': 'adapters/cloudflare-dev.ts',
|
|
58
56
|
'adapters/nitro': 'adapters/nitro.ts',
|
|
@@ -163,6 +161,50 @@ export function timberShims(_ctx: PluginContext): Plugin {
|
|
|
163
161
|
// In the client (browser) environment, all imports resolve to dist/
|
|
164
162
|
// via package.json exports (no remapping needed).
|
|
165
163
|
|
|
164
|
+
// ── #imports resolution for package-internal modules ─────────────
|
|
165
|
+
//
|
|
166
|
+
// #server-internal and #client-internal are Node package imports
|
|
167
|
+
// (package.json "imports" field). They are only resolvable from
|
|
168
|
+
// within this package — external consumers get module-not-found.
|
|
169
|
+
//
|
|
170
|
+
// In Vite, we intercept them here to resolve to src/ for dev/build
|
|
171
|
+
// correctness (same rationale as the @timber-js/app/* remap above).
|
|
172
|
+
if (cleanId === '#server-internal') {
|
|
173
|
+
const envName = (this as unknown as { environment?: { name?: string } }).environment?.name;
|
|
174
|
+
if (envName === 'client') {
|
|
175
|
+
return '\0timber:server-empty';
|
|
176
|
+
}
|
|
177
|
+
return resolve(PKG_ROOT, 'src', 'server', 'internal.ts');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (cleanId === '#client-internal') {
|
|
181
|
+
// Always resolve to src/ — framework internals only,
|
|
182
|
+
// no 'use client' directives to preserve.
|
|
183
|
+
return resolve(PKG_ROOT, 'src', 'client', 'internal.ts');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (cleanId === '#routing') {
|
|
187
|
+
return resolve(PKG_ROOT, 'src', 'routing', 'index.ts');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Legacy aliases — vitest and monorepo configs may still reference
|
|
191
|
+
// the old @timber-js/app/server/internal and @timber-js/app/client/internal
|
|
192
|
+
// specifiers via resolve.alias. When those aliases resolve to the
|
|
193
|
+
// absolute src/ path, they bypass this plugin entirely. But if the
|
|
194
|
+
// specifier reaches us unresolved (e.g., in a Vite build without
|
|
195
|
+
// aliases), handle it the same way as the #imports above.
|
|
196
|
+
if (cleanId === '@timber-js/app/server/internal') {
|
|
197
|
+
const envName = (this as unknown as { environment?: { name?: string } }).environment?.name;
|
|
198
|
+
if (envName === 'client') {
|
|
199
|
+
return '\0timber:server-empty';
|
|
200
|
+
}
|
|
201
|
+
return resolve(PKG_ROOT, 'src', 'server', 'internal.ts');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (cleanId === '@timber-js/app/client/internal') {
|
|
205
|
+
return resolve(PKG_ROOT, 'src', 'client', 'internal.ts');
|
|
206
|
+
}
|
|
207
|
+
|
|
166
208
|
if (cleanId === '@timber-js/app/server') {
|
|
167
209
|
const envName = (this as unknown as { environment?: { name?: string } }).environment?.name;
|
|
168
210
|
if (envName === 'client') {
|
|
@@ -245,6 +287,12 @@ const msg = "[timber] @timber-js/app/server was imported from client code. " +
|
|
|
245
287
|
function stub() { throw new Error(msg); }
|
|
246
288
|
export const headers = stub;
|
|
247
289
|
export const cookies = stub;
|
|
290
|
+
export const getHeaders = stub;
|
|
291
|
+
export const getHeader = stub;
|
|
292
|
+
export const getCookies = stub;
|
|
293
|
+
export const getCookie = stub;
|
|
294
|
+
export const getSearchParams = stub;
|
|
295
|
+
export const getSegmentParams = stub;
|
|
248
296
|
export const searchParams = stub;
|
|
249
297
|
export const deny = stub;
|
|
250
298
|
export const notFound = stub;
|
|
@@ -252,7 +300,7 @@ export const redirect = stub;
|
|
|
252
300
|
export const permanentRedirect = stub;
|
|
253
301
|
export const redirectExternal = stub;
|
|
254
302
|
export const waitUntil = stub;
|
|
255
|
-
export const
|
|
303
|
+
export const DenyOptions = {};
|
|
256
304
|
export const RedirectType = {};
|
|
257
305
|
export const DenySignal = stub;
|
|
258
306
|
export const RedirectSignal = stub;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* timber-static-build — Vite sub-plugin for static output mode.
|
|
3
3
|
*
|
|
4
4
|
* When `output: 'static'` is set in timber.config.ts, this plugin:
|
|
5
|
-
* 1. Validates that no dynamic APIs (
|
|
5
|
+
* 1. Validates that no dynamic APIs (getCookies(), getHeaders()) are used
|
|
6
6
|
* 2. When client JavaScript is disabled, rejects 'use client' and 'use server' directives
|
|
7
7
|
* 3. Coordinates build-time rendering of all pages
|
|
8
8
|
*
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { Plugin } from 'vite';
|
|
13
|
-
import type { PluginContext } from '../
|
|
13
|
+
import type { PluginContext } from '../plugin-context.js';
|
|
14
14
|
import { detectFileDirective } from '../utils/directive-parser.js';
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
@@ -29,7 +29,7 @@ export interface StaticOptions {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
|
-
// Detection: dynamic APIs (
|
|
32
|
+
// Detection: dynamic APIs (getCookies, getHeaders, and legacy cookies/headers)
|
|
33
33
|
// ---------------------------------------------------------------------------
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -39,12 +39,14 @@ export interface StaticOptions {
|
|
|
39
39
|
* We detect both import-level and call-level usage.
|
|
40
40
|
*/
|
|
41
41
|
const DYNAMIC_API_PATTERNS: Array<{ pattern: RegExp; name: string }> = [
|
|
42
|
+
{ pattern: /\bgetCookies\s*\(/, name: 'getCookies()' },
|
|
43
|
+
{ pattern: /\bgetHeaders\s*\(/, name: 'getHeaders()' },
|
|
42
44
|
{ pattern: /\bcookies\s*\(/, name: 'cookies()' },
|
|
43
45
|
{ pattern: /\bheaders\s*\(/, name: 'headers()' },
|
|
44
46
|
];
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
|
-
* Detect usage of dynamic per-request APIs (
|
|
49
|
+
* Detect usage of dynamic per-request APIs (getCookies(), getHeaders())
|
|
48
50
|
* that cannot work at build time in static mode.
|
|
49
51
|
*
|
|
50
52
|
* Returns an array of validation errors.
|
|
@@ -134,7 +136,7 @@ export function detectDirectives(
|
|
|
134
136
|
* Run all static mode validations on a source file.
|
|
135
137
|
*
|
|
136
138
|
* Combines:
|
|
137
|
-
* - Dynamic API detection (
|
|
139
|
+
* - Dynamic API detection (getCookies, getHeaders) — always in static mode
|
|
138
140
|
* - Directive detection ('use client', 'use server') — only when client JS is disabled
|
|
139
141
|
*/
|
|
140
142
|
export function validateStaticMode(
|
|
@@ -170,7 +172,7 @@ export function timberStaticBuild(ctx: PluginContext): Plugin {
|
|
|
170
172
|
* Validate source files during transform.
|
|
171
173
|
*
|
|
172
174
|
* In static mode, we check every app/ file for:
|
|
173
|
-
* - Dynamic API usage (
|
|
175
|
+
* - Dynamic API usage (getCookies(), getHeaders()) → build error
|
|
174
176
|
* - When client JS disabled: 'use client' / 'use server' directives → build error
|
|
175
177
|
*/
|
|
176
178
|
transform(code: string, id: string) {
|
|
@@ -15,33 +15,33 @@ import { fromSchema, isStandardSchema, isCodec } from '../schema-bridge.js';
|
|
|
15
15
|
import type { StandardSchemaV1 } from '../schema-bridge.js';
|
|
16
16
|
import type { Codec } from '../codec.js';
|
|
17
17
|
|
|
18
|
-
// Server-only reference for .
|
|
19
|
-
// In client environments, .
|
|
18
|
+
// Server-only reference for .get() — avoids pulling server ALS into client bundles.
|
|
19
|
+
// In client environments, .get() throws before reaching this code path.
|
|
20
20
|
//
|
|
21
|
-
// IMPORTANT: This is set eagerly via
|
|
21
|
+
// IMPORTANT: This is set eagerly via _setGetSearchParamsFn() at server startup
|
|
22
22
|
// (called from request-context.ts module initialization). It must NOT use
|
|
23
23
|
// dynamic `await import()` at call time because the async microtask from the
|
|
24
24
|
// dynamic import loses AsyncLocalStorage context in React's RSC Flight renderer,
|
|
25
|
-
// breaking
|
|
26
|
-
let
|
|
25
|
+
// breaking getSearchParams() in parallel slot pages. See TIM-523.
|
|
26
|
+
let _getSearchParamsFn: (() => Promise<URLSearchParams>) | undefined;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Register the
|
|
29
|
+
* Register the getSearchParams function. Called once at module load time
|
|
30
30
|
* from request-context.ts to avoid dynamic import at call time.
|
|
31
31
|
* @internal
|
|
32
32
|
*/
|
|
33
|
-
export function
|
|
34
|
-
|
|
33
|
+
export function _setGetSearchParamsFn(fn: () => Promise<URLSearchParams>): void {
|
|
34
|
+
_getSearchParamsFn = fn;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function
|
|
38
|
-
if (!
|
|
37
|
+
function getSearchParamsFromAls(): Promise<URLSearchParams> {
|
|
38
|
+
if (!_getSearchParamsFn) {
|
|
39
39
|
throw new Error(
|
|
40
|
-
'[timber] searchParams.
|
|
40
|
+
'[timber] searchParams.get() is only available on the server. ' +
|
|
41
41
|
'Use searchParams.useQueryStates() on the client.'
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
|
-
return
|
|
44
|
+
return _getSearchParamsFn();
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// ---------------------------------------------------------------------------
|
|
@@ -109,9 +109,9 @@ export interface SearchParamsDefinition<T extends Record<string, unknown>> {
|
|
|
109
109
|
parse(raw: Promise<URLSearchParams | Record<string, string | string[] | undefined>>): Promise<T>;
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
112
|
+
* Get typed search params from the current request context (ALS-backed).
|
|
113
113
|
*
|
|
114
|
-
* Server-only — reads
|
|
114
|
+
* Server-only — reads getSearchParams() from ALS and parses through codecs.
|
|
115
115
|
* Throws on client. Eliminates the naming conflict between the definition
|
|
116
116
|
* export and the server helper.
|
|
117
117
|
*
|
|
@@ -119,11 +119,11 @@ export interface SearchParamsDefinition<T extends Record<string, unknown>> {
|
|
|
119
119
|
* // app/products/page.tsx
|
|
120
120
|
* import { searchParams } from './params'
|
|
121
121
|
* export default async function Page() {
|
|
122
|
-
* const { page, category } = await searchParams.
|
|
122
|
+
* const { page, category } = await searchParams.get()
|
|
123
123
|
* }
|
|
124
124
|
* ```
|
|
125
125
|
*/
|
|
126
|
-
|
|
126
|
+
get(): Promise<T>;
|
|
127
127
|
|
|
128
128
|
/** Client hook — reads current URL params and returns typed values + setter. */
|
|
129
129
|
useQueryStates(options?: QueryStatesOptions): [T, SetParams<T>];
|
|
@@ -451,23 +451,23 @@ function buildDefinition<T extends Record<string, unknown>>(
|
|
|
451
451
|
];
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
-
// ----
|
|
455
|
-
// ALS-backed: reads
|
|
454
|
+
// ---- get ----
|
|
455
|
+
// ALS-backed: reads getSearchParams() from the current request context
|
|
456
456
|
// and parses through codecs. Server-only — throws on client.
|
|
457
|
-
async function
|
|
457
|
+
async function get(): Promise<T> {
|
|
458
458
|
if (typeof window !== 'undefined') {
|
|
459
459
|
throw new Error(
|
|
460
|
-
'[timber] searchParams.
|
|
460
|
+
'[timber] searchParams.get() is server-only. ' +
|
|
461
461
|
'Use searchParams.useQueryStates() on the client.'
|
|
462
462
|
);
|
|
463
463
|
}
|
|
464
|
-
const raw = await
|
|
464
|
+
const raw = await getSearchParamsFromAls();
|
|
465
465
|
return parseSync(raw);
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
const definition: SearchParamsDefinition<T> = {
|
|
469
469
|
parse,
|
|
470
|
-
|
|
470
|
+
get,
|
|
471
471
|
useQueryStates,
|
|
472
472
|
extend,
|
|
473
473
|
pick,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @timber-js/app/search-params — Typed search params
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
|
|
3
|
+
// Codec is the canonical home for the Codec type — import from
|
|
4
|
+
// @timber-js/app/codec. Re-export removed per TIM-721.
|
|
5
5
|
|
|
6
6
|
// Core types and factory
|
|
7
7
|
export type {
|
|
@@ -25,4 +25,4 @@ export { defineSearchParams } from './define.js';
|
|
|
25
25
|
export { withDefault, withUrlKey } from './wrappers.js';
|
|
26
26
|
|
|
27
27
|
// Runtime registry (route-scoped useQueryStates)
|
|
28
|
-
export { registerSearchParams,
|
|
28
|
+
export { registerSearchParams, getSearchParamsDefinition } from './registry.js';
|
|
@@ -26,6 +26,6 @@ export function registerSearchParams(route: string, definition: SearchParamsDefi
|
|
|
26
26
|
* Returns undefined if the route hasn't been loaded yet.
|
|
27
27
|
*/
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
export function
|
|
29
|
+
export function getSearchParamsDefinition(route: string): SearchParamsDefinition<any> | undefined {
|
|
30
30
|
return registry.get(route);
|
|
31
31
|
}
|