react-bun-ssr 0.3.2 → 0.4.1

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.
@@ -1,15 +1,23 @@
1
1
  import {
2
2
  createContext,
3
+ useCallback,
3
4
  useContext,
4
5
  type ComponentType,
5
6
  type Context,
6
7
  type ReactElement,
7
8
  type ReactNode,
8
9
  } from "react";
9
- import type { Params, RenderPayload, RouteErrorResponse, RouteModuleBundle } from "./types";
10
+ import type {
11
+ Params,
12
+ RenderPayload,
13
+ RouteErrorResponse,
14
+ RouteModuleBundle,
15
+ } from "./types";
16
+ import type { RouteActionStateHandler } from "./action-stub";
17
+ import { createRouteWire } from "./route-wire-protocol";
10
18
 
11
19
  interface RuntimeState {
12
- data: unknown;
20
+ loaderData: unknown;
13
21
  params: Params;
14
22
  url: URL;
15
23
  error?: unknown;
@@ -50,7 +58,40 @@ function useRuntimeState(): RuntimeState {
50
58
  }
51
59
 
52
60
  export function useLoaderData<T = unknown>(): T {
53
- return useRuntimeState().data as T;
61
+ return useRuntimeState().loaderData as T;
62
+ }
63
+
64
+ function hardNavigate(location: string): void {
65
+ if (typeof window === "undefined") {
66
+ return;
67
+ }
68
+ window.location.assign(location);
69
+ }
70
+
71
+ function readCurrentWindowUrl(): URL | null {
72
+ if (typeof window === "undefined") {
73
+ return null;
74
+ }
75
+ return new URL(window.location.href);
76
+ }
77
+
78
+ function createBrowserRouteWire() {
79
+ return createRouteWire({
80
+ getCurrentUrl: readCurrentWindowUrl,
81
+ hardNavigate,
82
+ });
83
+ }
84
+
85
+ export function createRouteAction<TState = unknown>(): RouteActionStateHandler<TState> {
86
+ return createBrowserRouteWire().action<TState>();
87
+ }
88
+
89
+ export function useRouteAction<TState = unknown>(): RouteActionStateHandler<TState> {
90
+ const requestUrl = useRequestUrl();
91
+ const routeTarget = requestUrl.pathname + requestUrl.search + requestUrl.hash;
92
+ const action = createBrowserRouteWire().action<TState>(routeTarget);
93
+
94
+ return useCallback(action, [routeTarget]);
54
95
  }
55
96
 
56
97
  export function useParams<T extends Params = Params>(): T {
@@ -83,7 +124,7 @@ export function createRouteTree(
83
124
  } = {},
84
125
  ): ReactElement {
85
126
  const runtimeState: RuntimeState = {
86
- data: payload.data,
127
+ loaderData: payload.loaderData,
87
128
  params: payload.params,
88
129
  url: new URL(payload.url),
89
130
  error: options.error ?? payload.error,
@@ -2,17 +2,41 @@ import type { ComponentType, ReactNode } from "react";
2
2
 
3
3
  export type Params = Record<string, string>;
4
4
 
5
- export interface RequestContext {
5
+ export interface AppRouteLocals extends Record<string, unknown> {}
6
+
7
+ export interface ResponseCookieOptions {
8
+ path?: string;
9
+ domain?: string;
10
+ expires?: Date | string;
11
+ maxAge?: number;
12
+ secure?: boolean;
13
+ httpOnly?: boolean;
14
+ sameSite?: "Strict" | "Lax" | "None" | "strict" | "lax" | "none";
15
+ }
16
+
17
+ export interface ResponseCookies {
18
+ get(name: string): string | undefined;
19
+ set(name: string, value: string, options?: ResponseCookieOptions): void;
20
+ delete(name: string, options?: Omit<ResponseCookieOptions, "expires" | "maxAge">): void;
21
+ }
22
+
23
+ export interface ResponseContext {
24
+ headers: Pick<Headers, "set" | "append" | "delete" | "get" | "has">;
25
+ cookies: ResponseCookies;
26
+ }
27
+
28
+ export interface RequestContext<Locals extends Record<string, unknown> = AppRouteLocals> {
6
29
  request: Request;
7
30
  url: URL;
8
31
  params: Params;
9
32
  cookies: Map<string, string>;
10
- locals: Record<string, unknown>;
33
+ locals: Locals;
34
+ response: ResponseContext;
11
35
  }
12
36
 
13
- export interface LoaderContext extends RequestContext {}
37
+ export interface LoaderContext<Locals extends Record<string, unknown> = AppRouteLocals> extends RequestContext<Locals> {}
14
38
 
15
- export interface ActionContext extends RequestContext {
39
+ export interface ActionContext<Locals extends Record<string, unknown> = AppRouteLocals> extends RequestContext<Locals> {
16
40
  formData?: FormData;
17
41
  json?: unknown;
18
42
  }
@@ -37,8 +61,12 @@ export type LoaderResult =
37
61
  | null;
38
62
  export type ActionResult = LoaderResult | RedirectResult;
39
63
 
40
- export type Loader = (ctx: LoaderContext) => Promise<LoaderResult> | LoaderResult;
41
- export type Action = (ctx: ActionContext) => Promise<ActionResult> | ActionResult;
64
+ export type Loader<Locals extends Record<string, unknown> = AppRouteLocals> = (
65
+ ctx: LoaderContext<Locals>,
66
+ ) => Promise<LoaderResult> | LoaderResult;
67
+ export type Action<Locals extends Record<string, unknown> = AppRouteLocals> = (
68
+ ctx: ActionContext<Locals>,
69
+ ) => Promise<ActionResult> | ActionResult;
42
70
 
43
71
  export interface RedirectResult {
44
72
  type: "redirect";
@@ -46,8 +74,8 @@ export interface RedirectResult {
46
74
  status?: 301 | 302 | 303 | 307 | 308;
47
75
  }
48
76
 
49
- export type Middleware = (
50
- ctx: RequestContext,
77
+ export type Middleware<Locals extends Record<string, unknown> = AppRouteLocals> = (
78
+ ctx: RequestContext<Locals>,
51
79
  next: () => Promise<Response>,
52
80
  ) => Promise<Response> | Response;
53
81
 
@@ -109,7 +137,9 @@ export interface RouteModule {
109
137
 
110
138
  export type LayoutModule = RouteModule;
111
139
 
112
- export type ApiHandler = (ctx: RequestContext) => Promise<Response | unknown> | Response | unknown;
140
+ export type ApiHandler<Locals extends Record<string, unknown> = AppRouteLocals> = (
141
+ ctx: RequestContext<Locals>,
142
+ ) => Promise<Response | unknown> | Response | unknown;
113
143
 
114
144
  export interface ApiRouteModule {
115
145
  GET?: ApiHandler;
@@ -170,6 +200,7 @@ export interface PageRouteDefinition {
170
200
  type: "page";
171
201
  id: string;
172
202
  filePath: string;
203
+ serverFilePath?: string;
173
204
  routePath: string;
174
205
  segments: RouteSegment[];
175
206
  score: number;
@@ -212,12 +243,42 @@ export interface BuildManifest {
212
243
 
213
244
  export interface RenderPayload {
214
245
  routeId: string;
215
- data: unknown;
246
+ loaderData: unknown;
216
247
  params: Params;
217
248
  url: string;
218
249
  error?: unknown;
219
250
  }
220
251
 
252
+ export interface ActionDataEnvelope {
253
+ type: "data";
254
+ status: number;
255
+ data: unknown;
256
+ }
257
+
258
+ export interface ActionRedirectEnvelope {
259
+ type: "redirect";
260
+ status: number;
261
+ location: string;
262
+ }
263
+
264
+ export interface ActionCatchEnvelope {
265
+ type: "catch";
266
+ status: number;
267
+ error: RouteErrorResponse;
268
+ }
269
+
270
+ export interface ActionErrorEnvelope {
271
+ type: "error";
272
+ status: number;
273
+ message: string;
274
+ }
275
+
276
+ export type ActionResponseEnvelope =
277
+ | ActionDataEnvelope
278
+ | ActionRedirectEnvelope
279
+ | ActionCatchEnvelope
280
+ | ActionErrorEnvelope;
281
+
221
282
  export interface ClientRouteSnapshot {
222
283
  id: string;
223
284
  routePath: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-bun-ssr",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,