@bractjs/bractjs 0.1.26 → 0.1.28
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/README.md +283 -58
- package/bin/cli.ts +18 -1
- package/package.json +1 -1
- package/src/__tests__/build-path.test.ts +29 -0
- package/src/__tests__/codegen-write.test.ts +67 -0
- package/src/__tests__/codegen.test.ts +64 -1
- package/src/__tests__/compile-safety.test.ts +4 -0
- package/src/__tests__/csp.test.ts +10 -0
- package/src/__tests__/define-actions.test.ts +69 -0
- package/src/__tests__/env.test.ts +18 -0
- package/src/__tests__/fetcher-store.test.ts +67 -0
- package/src/__tests__/fixtures/app/root.tsx +7 -2
- package/src/__tests__/fixtures/app/routes/boom.tsx +9 -0
- package/src/__tests__/fixtures/app/routes/client-only.tsx +16 -0
- package/src/__tests__/fixtures/app/routes/counter.tsx +16 -0
- package/src/__tests__/fixtures/app/routes/data-only.tsx +16 -0
- package/src/__tests__/fixtures/app/routes/intent-demo.tsx +46 -0
- package/src/__tests__/fixtures/app/routes/protected-client-only.tsx +15 -0
- package/src/__tests__/fixtures/app/routes/search-demo.tsx +39 -0
- package/src/__tests__/form-data-helpers.test.ts +43 -0
- package/src/__tests__/integration.test.ts +56 -0
- package/src/__tests__/loader.test.ts +32 -1
- package/src/__tests__/nav-utils.test.ts +46 -0
- package/src/__tests__/prerender.test.ts +102 -0
- package/src/__tests__/programmatic-api.test.ts +20 -1
- package/src/__tests__/revalidation.test.ts +65 -0
- package/src/__tests__/route-lint.test.ts +74 -0
- package/src/__tests__/route-table.test.ts +33 -0
- package/src/__tests__/safe-validate.test.ts +96 -0
- package/src/__tests__/scroll-restoration.test.ts +66 -0
- package/src/__tests__/search-serializer.test.ts +42 -0
- package/src/__tests__/search-validation.test.ts +125 -0
- package/src/__tests__/security.test.ts +110 -1
- package/src/__tests__/selective-ssr.test.ts +85 -0
- package/src/__tests__/spa-mode.test.ts +77 -0
- package/src/__tests__/typed-routing.test.ts +239 -0
- package/src/build/bundler.ts +33 -0
- package/src/build/prerender.ts +88 -0
- package/src/build/route-lint.ts +49 -0
- package/src/client/ClientRouter.tsx +239 -47
- package/src/client/build-path.ts +24 -0
- package/src/client/cache.ts +8 -0
- package/src/client/components/Await.tsx +9 -2
- package/src/client/components/Form.tsx +23 -34
- package/src/client/components/Link.tsx +105 -11
- package/src/client/components/Outlet.tsx +8 -2
- package/src/client/components/ScrollRestoration.tsx +125 -0
- package/src/client/entry.tsx +39 -2
- package/src/client/fetcher-store.ts +61 -0
- package/src/client/form-utils.ts +3 -0
- package/src/client/hooks/useActionData.ts +7 -3
- package/src/client/hooks/useFetcher.ts +116 -33
- package/src/client/hooks/useFetchers.ts +23 -0
- package/src/client/hooks/useLoaderData.ts +8 -4
- package/src/client/hooks/useLocation.ts +27 -0
- package/src/client/hooks/useNavigate.ts +51 -0
- package/src/client/hooks/useParams.ts +15 -4
- package/src/client/hooks/useRevalidator.ts +26 -0
- package/src/client/hooks/useSearch.ts +73 -0
- package/src/client/hooks/useSearchParams.ts +21 -6
- package/src/client/nav-utils.ts +26 -0
- package/src/client/prefetch.ts +110 -15
- package/src/client/registry.ts +131 -0
- package/src/client/revalidation.ts +25 -0
- package/src/client/router.tsx +28 -1
- package/src/client/scroll-restoration.ts +48 -0
- package/src/client/search-serializer.ts +40 -0
- package/src/client/types.ts +6 -0
- package/src/codegen/route-codegen.ts +201 -29
- package/src/config/load.ts +21 -0
- package/src/dev/hmr-client.ts +3 -1
- package/src/dev/route-table.ts +27 -0
- package/src/dev/server.ts +106 -8
- package/src/dev/watcher.ts +25 -3
- package/src/index.ts +44 -3
- package/src/server/action-handler.ts +12 -3
- package/src/server/action-registry.ts +35 -0
- package/src/server/csp.ts +10 -1
- package/src/server/csrf.ts +26 -0
- package/src/server/env.ts +26 -5
- package/src/server/layout.ts +31 -1
- package/src/server/loader.ts +14 -8
- package/src/server/render.ts +18 -3
- package/src/server/request-handler.ts +50 -8
- package/src/server/search.ts +43 -0
- package/src/server/serve.ts +88 -1
- package/src/server/spa.ts +62 -0
- package/src/server/stream-handler.ts +10 -1
- package/src/server/validate.ts +85 -13
- package/src/shared/context.ts +5 -0
- package/src/shared/define-actions.ts +39 -0
- package/src/shared/form-data.ts +34 -0
- package/src/shared/route-types.ts +83 -2
- package/templates/new-app/app/root.tsx +2 -1
- package/templates/new-app/bractjs.config.ts +7 -12
- package/types/config.d.ts +21 -0
- package/types/index.d.ts +210 -10
- package/types/route.d.ts +62 -2
package/types/index.d.ts
CHANGED
|
@@ -4,8 +4,11 @@ import type { ReactNode, Context, CSSProperties } from "react";
|
|
|
4
4
|
export type {
|
|
5
5
|
LoaderArgs, ActionArgs, MetaDescriptor, MetaArgs,
|
|
6
6
|
LoaderFunction, ActionFunction, MetaFunction, RouteModule,
|
|
7
|
-
RouteFile, Segment,
|
|
7
|
+
RouteFile, Segment, RouterLocation,
|
|
8
|
+
ShouldRevalidateArgs, ShouldRevalidateFunction,
|
|
9
|
+
LoaderData, ActionData,
|
|
8
10
|
} from "./route.d.ts";
|
|
11
|
+
import type { RouterLocation, LoaderData, ActionData, ActionArgs } from "./route.d.ts";
|
|
9
12
|
|
|
10
13
|
// ── Config + Server ───────────────────────────────────────────────────────
|
|
11
14
|
export type { BractJSConfig, ServerManifest, BuildConfig } from "./config.d.ts";
|
|
@@ -18,9 +21,15 @@ export interface RenderOptions {
|
|
|
18
21
|
actionData: unknown;
|
|
19
22
|
params: Record<string, string>;
|
|
20
23
|
pathname: string;
|
|
24
|
+
/** Validated search params — hydrates `useSearch()` on the client. */
|
|
25
|
+
search?: Record<string, unknown>;
|
|
21
26
|
manifest: ServerManifest;
|
|
22
27
|
meta: MetaDescriptor[];
|
|
23
28
|
status?: number;
|
|
29
|
+
routeFile?: string;
|
|
30
|
+
nonce?: string;
|
|
31
|
+
/** Set when the document did not SSR the route component (selective SSR / SPA shell). */
|
|
32
|
+
ssrMode?: "client-only" | "data-only" | "spa";
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
export type OnErrorHook = (err: unknown, request?: Request) => Promise<void> | void;
|
|
@@ -64,6 +73,10 @@ export interface BractJSContextValue {
|
|
|
64
73
|
params: Record<string, string>;
|
|
65
74
|
pathname: string;
|
|
66
75
|
manifest: RouteManifest;
|
|
76
|
+
/** The request's location, so `useLocation()` works during SSR (hash is always ""). */
|
|
77
|
+
location?: RouterLocation;
|
|
78
|
+
/** Validated search params, so `useSearch()` works during SSR. */
|
|
79
|
+
search?: Record<string, unknown>;
|
|
67
80
|
}
|
|
68
81
|
export declare const BractJSContext: Context<BractJSContextValue>;
|
|
69
82
|
export declare function BractJSProvider(props: { value: BractJSContextValue; children: ReactNode }): ReactNode;
|
|
@@ -129,18 +142,116 @@ export declare function validate<T>(
|
|
|
129
142
|
input: FormData | Record<string, unknown>,
|
|
130
143
|
): Promise<T>;
|
|
131
144
|
|
|
145
|
+
export type SafeValidateResult<T> =
|
|
146
|
+
| { ok: true; data: T }
|
|
147
|
+
| { ok: false; fieldErrors: FieldErrors; firstError: string };
|
|
148
|
+
/** Non-throwing validate(): returns a result instead of throwing a 400. */
|
|
149
|
+
export declare function safeValidate<T>(
|
|
150
|
+
schema: { safeParse?(i: unknown): unknown } | { parse(i: unknown): T },
|
|
151
|
+
input: FormData | Record<string, unknown>,
|
|
152
|
+
): Promise<SafeValidateResult<T>>;
|
|
153
|
+
/** True for the 400 Response thrown by validate()/searchSchema validation. */
|
|
154
|
+
export declare function isValidationResponse(value: unknown): value is Response;
|
|
155
|
+
/** Parse the `{ errors }` body of a validation 400 into field errors + first message. */
|
|
156
|
+
export declare function readValidationError(
|
|
157
|
+
res: Response,
|
|
158
|
+
): Promise<{ fieldErrors: FieldErrors; firstError: string }>;
|
|
159
|
+
|
|
160
|
+
// ── FormData helpers ──────────────────────────────────────────────────────
|
|
161
|
+
/** String field from FormData; "" when missing or a File. */
|
|
162
|
+
export declare function formText(formData: FormData, key: string): string;
|
|
163
|
+
/** Collect string fields from FormData (all, or a named subset). */
|
|
164
|
+
export declare function formValues(formData: FormData, keys?: string[]): Record<string, string>;
|
|
165
|
+
|
|
166
|
+
// ── Search-param validation ───────────────────────────────────────────────
|
|
167
|
+
/** URLSearchParams → plain object; repeated keys collapse into arrays. */
|
|
168
|
+
export declare function searchParamsToObject(sp: URLSearchParams): Record<string, string | string[]>;
|
|
169
|
+
/**
|
|
170
|
+
* Validate a URL's search params against a route's `searchSchema`. No schema →
|
|
171
|
+
* the raw string record; failure → throws a 400 Response with field errors.
|
|
172
|
+
*/
|
|
173
|
+
export declare function validateSearch(schema: unknown, url: URL): Promise<Record<string, unknown>>;
|
|
174
|
+
/** Serialize a search object back into a query string (leading `?`, or ""). */
|
|
175
|
+
export declare function serializeSearch(search: Record<string, unknown>): string;
|
|
176
|
+
|
|
177
|
+
// ── Typed-routing registration seam ───────────────────────────────────────
|
|
178
|
+
// Mirror of src/client/registry.ts. Augment `Register` (done by `bractjs codegen`
|
|
179
|
+
// in app/route-types.gen.ts) to make <Link>/useNavigate/useParams/useSearchParams
|
|
180
|
+
// type-safe. Un-augmented, everything falls back to loose `string` / Record so
|
|
181
|
+
// apps that never run codegen keep compiling. Keep in sync with registry.ts.
|
|
182
|
+
export interface Register {}
|
|
183
|
+
export interface RouteRegistry {
|
|
184
|
+
routes: string;
|
|
185
|
+
params: Record<string, Record<string, string>>;
|
|
186
|
+
search: Record<string, Record<string, string>>;
|
|
187
|
+
}
|
|
188
|
+
export interface RouteSearchParamsMap {}
|
|
189
|
+
export interface RouteContextMap {}
|
|
190
|
+
// Infer each member directly (NOT `infer R extends RouteRegistry` — a constrained
|
|
191
|
+
// infer fails to match the generated registry and falls back to loose). Keep in
|
|
192
|
+
// sync with src/client/registry.ts.
|
|
193
|
+
export type RegisteredRoutes =
|
|
194
|
+
Register extends { routes: { routes: infer R } } ? R : string;
|
|
195
|
+
export type RegisteredParamsMap =
|
|
196
|
+
Register extends { routes: { params: infer P } } ? P : Record<string, Record<string, string>>;
|
|
197
|
+
export type RegisteredSearchMap =
|
|
198
|
+
Register extends { routes: { search: infer S } } ? S : Record<string, Record<string, string>>;
|
|
199
|
+
export type RegisteredSearchOutputMap =
|
|
200
|
+
Register extends { routes: { searchOutput: infer S } } ? S : Record<string, Record<string, unknown>>;
|
|
201
|
+
export type ParamsFor<TTo> =
|
|
202
|
+
TTo extends keyof RegisteredParamsMap ? RegisteredParamsMap[TTo] : Record<string, string>;
|
|
203
|
+
export type SearchFor<TTo> =
|
|
204
|
+
TTo extends keyof RegisteredSearchMap ? RegisteredSearchMap[TTo] : Record<string, string>;
|
|
205
|
+
/** Validated (schema-output) search object for a specific route literal. */
|
|
206
|
+
export type SearchOutputFor<TTo> =
|
|
207
|
+
TTo extends keyof RegisteredSearchOutputMap ? RegisteredSearchOutputMap[TTo] : Record<string, unknown>;
|
|
208
|
+
/** Infer the output type of a Zod/Valibot-compatible schema (duck-typed z.infer). */
|
|
209
|
+
export type InferSchemaOutput<S> =
|
|
210
|
+
S extends { parse(input: unknown): infer T } ? T :
|
|
211
|
+
S extends { safeParse(input: unknown): infer R }
|
|
212
|
+
? (Awaited<R> extends { data?: infer T } ? NonNullable<T> : Record<string, unknown>)
|
|
213
|
+
: Record<string, unknown>;
|
|
214
|
+
export declare function buildPath(pattern: string, params: Record<string, string | number>): string;
|
|
215
|
+
|
|
132
216
|
// ── Client components ─────────────────────────────────────────────────────
|
|
133
217
|
export declare function Scripts(): null;
|
|
134
218
|
export declare function LiveReload(): ReactNode;
|
|
135
219
|
export declare function Outlet(): ReactNode;
|
|
136
220
|
|
|
137
|
-
export
|
|
138
|
-
|
|
221
|
+
export type LinkProps<TTo extends RegisteredRoutes = RegisteredRoutes> = {
|
|
222
|
+
to: TTo | (string & {});
|
|
223
|
+
params?: ParamsFor<TTo>;
|
|
224
|
+
/** Search params for the target, typed by its `searchSchema` (replaces any query in `to`). */
|
|
225
|
+
search?: Partial<SearchOutputFor<TTo>>;
|
|
226
|
+
/** When to prefetch the target's chunk + loader data. Default "none". */
|
|
227
|
+
prefetch?: "none" | "intent" | "hover" | "viewport" | "render";
|
|
228
|
+
viewTransition?: boolean;
|
|
229
|
+
/** Replace the current history entry instead of pushing. */
|
|
230
|
+
replace?: boolean;
|
|
231
|
+
children?: ReactNode;
|
|
232
|
+
className?: string;
|
|
233
|
+
[key: string]: unknown;
|
|
234
|
+
};
|
|
235
|
+
export declare function Link<TTo extends RegisteredRoutes = RegisteredRoutes>(props: LinkProps<TTo>): ReactNode;
|
|
139
236
|
|
|
140
|
-
export interface
|
|
237
|
+
export interface ScrollRestorationProps {
|
|
238
|
+
/** Derive the storage key for a location. Default: `location.key`. */
|
|
239
|
+
getKey?: (location: RouterLocation) => string;
|
|
240
|
+
storageKey?: string;
|
|
241
|
+
}
|
|
242
|
+
/** Restores scroll on back/forward, scrolls to top (or `#hash`) on new navigations. Render once in root.tsx. */
|
|
243
|
+
export declare function ScrollRestoration(props?: ScrollRestorationProps): null;
|
|
244
|
+
|
|
245
|
+
export interface FormProps { method?: "post" | "put" | "delete"; action?: string; /** Renders a hidden `intent` input (pairs with defineActions()). */ intent?: string; children?: ReactNode; [key: string]: unknown; }
|
|
141
246
|
export declare function Form(props: FormProps): ReactNode;
|
|
142
247
|
|
|
143
|
-
|
|
248
|
+
// ── defineActions (intent dispatch) ───────────────────────────────────────
|
|
249
|
+
/** Compose a route action from per-intent handlers, dispatching on the form's `intent` field. */
|
|
250
|
+
export declare function defineActions<M extends Record<string, (args: ActionArgs) => unknown>>(
|
|
251
|
+
handlers: M,
|
|
252
|
+
): (args: ActionArgs) => Promise<Awaited<ReturnType<M[keyof M]>> | Response>;
|
|
253
|
+
|
|
254
|
+
export interface AwaitProps<T> { resolve: Promise<T> | Deferred<T>; fallback: ReactNode; children: (data: T) => ReactNode; }
|
|
144
255
|
export declare function Await<T>(props: AwaitProps<T>): ReactNode;
|
|
145
256
|
|
|
146
257
|
export type ImageFormat = "webp" | "avif" | "jpeg" | "png";
|
|
@@ -161,29 +272,97 @@ export interface ImageProps {
|
|
|
161
272
|
export declare function Image(props: ImageProps): ReactNode;
|
|
162
273
|
|
|
163
274
|
// ── Client hooks ──────────────────────────────────────────────────────────
|
|
164
|
-
|
|
165
|
-
export declare function
|
|
166
|
-
export declare function
|
|
275
|
+
// Pass the loader/action function type to infer the data — useLoaderData<typeof loader>().
|
|
276
|
+
export declare function useLoaderData<T = unknown>(): LoaderData<T>;
|
|
277
|
+
export declare function useActionData<T = unknown>(): ActionData<T> | null;
|
|
278
|
+
/** The current location — reactive on the client, request-derived during SSR. */
|
|
279
|
+
export declare function useLocation(): RouterLocation;
|
|
280
|
+
export declare function useParams<TTo extends string>(): ParamsFor<TTo>;
|
|
281
|
+
export declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
|
|
167
282
|
export type NavigationState = "idle" | "loading" | "submitting";
|
|
168
283
|
export declare function useNavigation(): { state: NavigationState };
|
|
284
|
+
|
|
285
|
+
export interface NavigateOptions<TTo extends RegisteredRoutes = RegisteredRoutes> {
|
|
286
|
+
params?: ParamsFor<TTo>;
|
|
287
|
+
/** Search params for the target, typed by its `searchSchema` (replaces any query in `to`). */
|
|
288
|
+
search?: Partial<SearchOutputFor<TTo>>;
|
|
289
|
+
/** Replace the current history entry instead of pushing a new one. */
|
|
290
|
+
replace?: boolean;
|
|
291
|
+
/** Arbitrary history state, readable via `useLocation().state` after navigating. */
|
|
292
|
+
state?: unknown;
|
|
293
|
+
}
|
|
294
|
+
export interface NavigateFn {
|
|
295
|
+
<TTo extends RegisteredRoutes>(to: TTo | (string & {}), options?: NavigateOptions<TTo>): Promise<void>;
|
|
296
|
+
}
|
|
297
|
+
export declare function useNavigate(): NavigateFn;
|
|
298
|
+
|
|
299
|
+
// ── Fetchers ──────────────────────────────────────────────────────────────
|
|
300
|
+
export type FetcherState = "idle" | "loading" | "submitting";
|
|
301
|
+
export interface FetcherEntry {
|
|
302
|
+
key: string;
|
|
303
|
+
state: FetcherState;
|
|
304
|
+
data: unknown;
|
|
305
|
+
/** The submitted form data while a submission is in flight — the optimistic-UI source. */
|
|
306
|
+
formData?: FormData;
|
|
307
|
+
formMethod?: string;
|
|
308
|
+
}
|
|
309
|
+
export interface FetcherFormProps {
|
|
310
|
+
method?: "post" | "put" | "delete";
|
|
311
|
+
action?: string;
|
|
312
|
+
/** Renders a hidden `intent` input (pairs with defineActions()). */
|
|
313
|
+
intent?: string;
|
|
314
|
+
children?: ReactNode;
|
|
315
|
+
[key: string]: unknown;
|
|
316
|
+
}
|
|
169
317
|
export interface FetcherResult {
|
|
170
318
|
data: unknown;
|
|
171
|
-
state:
|
|
319
|
+
state: FetcherState;
|
|
320
|
+
formData?: FormData;
|
|
321
|
+
formMethod?: string;
|
|
322
|
+
key: string;
|
|
172
323
|
load(path: string): Promise<void>;
|
|
173
324
|
submit(path: string, opts: { method: string; body: FormData | Record<string, string> }): Promise<void>;
|
|
325
|
+
/** A form that submits through this fetcher (no navigation, no history). */
|
|
326
|
+
Form: (props: FetcherFormProps) => ReactNode;
|
|
174
327
|
}
|
|
175
328
|
export interface StreamFetcherResult<T = unknown> {
|
|
329
|
+
/** @deprecated Never emitted — call `connect(actionId)` instead. Removed in 0.2. */
|
|
176
330
|
events: AsyncGenerator<T>;
|
|
177
331
|
connect(actionId: string): AsyncGenerator<T>;
|
|
178
332
|
}
|
|
179
|
-
export
|
|
333
|
+
export interface UseFetcherOptions { key?: string; stream?: boolean }
|
|
334
|
+
export declare function useFetcher(opts?: { key?: string }): FetcherResult;
|
|
180
335
|
export declare function useFetcher<T>(opts: { stream: true }): StreamFetcherResult<T>;
|
|
336
|
+
/** Every active fetcher — the cross-component view for optimistic UI. */
|
|
337
|
+
export declare function useFetchers(): FetcherEntry[];
|
|
338
|
+
|
|
339
|
+
// ── Revalidation ──────────────────────────────────────────────────────────
|
|
340
|
+
export interface Revalidator {
|
|
341
|
+
revalidate(): Promise<void>;
|
|
342
|
+
state: "idle" | "loading";
|
|
343
|
+
}
|
|
344
|
+
/** Manually re-run the active route's loaders (respects `shouldRevalidate`). */
|
|
345
|
+
export declare function useRevalidator(): Revalidator;
|
|
346
|
+
|
|
347
|
+
// ── Typed search ──────────────────────────────────────────────────────────
|
|
348
|
+
/** The current route's VALIDATED search params (its `searchSchema` output). */
|
|
349
|
+
export declare function useSearch<TTo extends string>(): SearchOutputFor<TTo>;
|
|
350
|
+
export declare function useSearch<T extends Record<string, unknown>>(): T;
|
|
351
|
+
export interface SetSearchOptions { replace?: boolean }
|
|
352
|
+
export type SetSearchFn<T extends Record<string, unknown>> = (
|
|
353
|
+
updater: Partial<T> | ((prev: T) => Partial<T>),
|
|
354
|
+
options?: SetSearchOptions,
|
|
355
|
+
) => Promise<void>;
|
|
356
|
+
/** Merge a patch into the current search params and soft-navigate (loaders re-run). */
|
|
357
|
+
export declare function useSetSearch<TTo extends string>(): SetSearchFn<SearchOutputFor<TTo>>;
|
|
358
|
+
export declare function useSetSearch<T extends Record<string, unknown>>(): SetSearchFn<T>;
|
|
181
359
|
|
|
182
360
|
export interface SearchParamsResult<T extends Record<string, string> = Record<string, string>> {
|
|
183
361
|
searchParams: URLSearchParams;
|
|
184
362
|
getParam<K extends keyof T & string>(key: K): T[K] | null;
|
|
185
363
|
setSearchParams(updater: Record<string, string> | ((prev: URLSearchParams) => URLSearchParams)): void;
|
|
186
364
|
}
|
|
365
|
+
export declare function useSearchParams<TTo extends string>(): SearchParamsResult<SearchFor<TTo>>;
|
|
187
366
|
export declare function useSearchParams<T extends Record<string, string> = Record<string, string>>(): SearchParamsResult<T>;
|
|
188
367
|
|
|
189
368
|
// ── Typed route context ───────────────────────────────────────────────────
|
|
@@ -272,3 +451,24 @@ export interface DevServer {
|
|
|
272
451
|
export declare function createDevServer(options?: DevServerOptions): Promise<DevServer>;
|
|
273
452
|
|
|
274
453
|
export declare function loadUserConfig(): Promise<Partial<BractJSConfig>>;
|
|
454
|
+
|
|
455
|
+
/** Identity helper for bractjs.config.ts — wrap your default export for autocomplete + type-checking. */
|
|
456
|
+
export declare function defineConfig(config: Partial<BractJSConfig>): Partial<BractJSConfig>;
|
|
457
|
+
|
|
458
|
+
// ── Prerendering / SPA shell ──────────────────────────────────────────────
|
|
459
|
+
export interface PrerenderOptions {
|
|
460
|
+
prerender: string[] | (() => string[] | Promise<string[]>);
|
|
461
|
+
appDir?: string;
|
|
462
|
+
publicDir?: string;
|
|
463
|
+
buildDir?: string;
|
|
464
|
+
manifest?: ServerManifest;
|
|
465
|
+
}
|
|
466
|
+
export interface PrerenderResult { written: string[] }
|
|
467
|
+
/** Build-time prerendering (SSG): write HTML + /_data payloads under `<buildDir>/client/_prerender/`. */
|
|
468
|
+
export declare function runPrerender(options: PrerenderOptions): Promise<PrerenderResult>;
|
|
469
|
+
/** Render the SPA-mode document shell (config `ssr: false`). */
|
|
470
|
+
export declare function renderSpaShell(
|
|
471
|
+
appDir: string,
|
|
472
|
+
manifest: ServerManifest,
|
|
473
|
+
registry?: ModuleRegistry,
|
|
474
|
+
): Promise<string>;
|
package/types/route.d.ts
CHANGED
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** A parsed navigation location (see `useLocation`). `hash` is always "" during SSR. */
|
|
4
|
+
export interface RouterLocation {
|
|
5
|
+
pathname: string;
|
|
6
|
+
/** Raw query string including the leading `?`, or `""`. */
|
|
7
|
+
search: string;
|
|
8
|
+
/** Fragment including the leading `#`, or `""`. */
|
|
9
|
+
hash: string;
|
|
10
|
+
state: unknown;
|
|
11
|
+
key: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface LoaderArgs<TSearch extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
15
|
request: Request;
|
|
5
16
|
params: Record<string, string>;
|
|
6
17
|
context: Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* The request's search params, validated/coerced by the route's
|
|
20
|
+
* `searchSchema` export when present; otherwise the raw string record
|
|
21
|
+
* (repeated keys become arrays). Parameterize to skip the cast:
|
|
22
|
+
* `loader({ search }: LoaderArgs<BoardSearch>)`.
|
|
23
|
+
*/
|
|
24
|
+
search: TSearch;
|
|
7
25
|
}
|
|
8
26
|
|
|
9
|
-
export interface ActionArgs extends
|
|
27
|
+
export interface ActionArgs<TSearch extends Record<string, unknown> = Record<string, unknown>>
|
|
28
|
+
extends LoaderArgs<TSearch> {
|
|
10
29
|
formData: FormData;
|
|
11
30
|
}
|
|
12
31
|
|
|
32
|
+
/**
|
|
33
|
+
* The data a route's loader resolves to, for typing `useLoaderData`. Pass the
|
|
34
|
+
* loader function type to infer it (`useLoaderData<typeof loader>()` →
|
|
35
|
+
* awaited return, `Response` excluded, `Deferred` fields preserved). A plain
|
|
36
|
+
* object type is returned as-is (back-compat).
|
|
37
|
+
*/
|
|
38
|
+
export type LoaderData<T> = T extends (...args: never[]) => unknown
|
|
39
|
+
? Exclude<Awaited<ReturnType<T>>, Response>
|
|
40
|
+
: T;
|
|
41
|
+
|
|
42
|
+
/** The data a route's action resolves to, for typing `useActionData`. See {@link LoaderData}. */
|
|
43
|
+
export type ActionData<T> = LoaderData<T>;
|
|
44
|
+
|
|
13
45
|
export type MetaDescriptor =
|
|
14
46
|
| { title: string }
|
|
15
47
|
| { name: string; content: string }
|
|
@@ -35,17 +67,45 @@ export interface BeforeLoadArgs {
|
|
|
35
67
|
params: Record<string, string>;
|
|
36
68
|
context: Record<string, unknown>;
|
|
37
69
|
location: { pathname: string; search: string };
|
|
70
|
+
/** Validated search params (server-side only; absent in the client-side guard). */
|
|
71
|
+
search?: Record<string, unknown>;
|
|
38
72
|
}
|
|
39
73
|
|
|
40
74
|
export type BeforeLoadFunction = (
|
|
41
75
|
args: BeforeLoadArgs,
|
|
42
76
|
) => void | Response | Promise<void | Response>;
|
|
43
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Decide whether loader data should be refetched (SWR background refetch and
|
|
80
|
+
* post-mutation revalidation). Return `args.defaultShouldRevalidate` (true)
|
|
81
|
+
* for the default behavior.
|
|
82
|
+
*/
|
|
83
|
+
export interface ShouldRevalidateArgs {
|
|
84
|
+
currentUrl: URL;
|
|
85
|
+
nextUrl: URL;
|
|
86
|
+
formMethod?: string;
|
|
87
|
+
actionStatus?: number;
|
|
88
|
+
defaultShouldRevalidate: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type ShouldRevalidateFunction = (args: ShouldRevalidateArgs) => boolean;
|
|
92
|
+
|
|
44
93
|
export interface RouteModule<TLoader = unknown, TAction = unknown> {
|
|
45
94
|
loader?: LoaderFunction<TLoader>;
|
|
46
95
|
action?: ActionFunction<TAction>;
|
|
47
96
|
meta?: MetaFunction<TLoader>;
|
|
48
97
|
beforeLoad?: BeforeLoadFunction;
|
|
98
|
+
shouldRevalidate?: ShouldRevalidateFunction;
|
|
99
|
+
/** Zod/Valibot-compatible schema validating search params before loaders run (400 on failure). */
|
|
100
|
+
searchSchema?: unknown;
|
|
101
|
+
/**
|
|
102
|
+
* Selective SSR: `true` (default) full SSR; `"data-only"` loaders run on the
|
|
103
|
+
* server but the component renders client-only; `false` neither the route
|
|
104
|
+
* loader nor the component runs during document SSR (beforeLoad still does).
|
|
105
|
+
*/
|
|
106
|
+
ssr?: boolean | "data-only";
|
|
107
|
+
/** SSR'd in the component's place for `ssr: false` / `"data-only"` routes. */
|
|
108
|
+
Fallback?: ComponentType;
|
|
49
109
|
handle?: Record<string, unknown>;
|
|
50
110
|
ErrorBoundary?: ComponentType<{ error: unknown }>;
|
|
51
111
|
default?: ComponentType;
|