rwsdk 1.0.0-beta.42 → 1.0.0-beta.44

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.
@@ -13,7 +13,11 @@ export declare const fetchTransport: Transport;
13
13
  * making the page interactive. Call this from your client entry point.
14
14
  *
15
15
  * @param transport - Custom transport for server communication (defaults to fetchTransport)
16
- * @param hydrateRootOptions - Options passed to React's hydrateRoot
16
+ * @param hydrateRootOptions - Options passed to React's `hydrateRoot`. Supports all React hydration options including:
17
+ * - `onUncaughtError`: Handler for uncaught errors (async errors, event handler errors).
18
+ * If not provided, defaults to logging errors to console.
19
+ * - `onCaughtError`: Handler for errors caught by error boundaries
20
+ * - `onRecoverableError`: Handler for recoverable errors
17
21
  * @param handleResponse - Custom response handler for navigation errors (navigation GETs)
18
22
  * @param onHydrationUpdate - Callback invoked after a new RSC payload has been committed on the client
19
23
  * @param onActionResponse - Optional hook invoked when an action returns a Response;
@@ -34,6 +38,23 @@ export declare const fetchTransport: Transport;
34
38
  * initClient({ handleResponse });
35
39
  *
36
40
  * @example
41
+ * // With error handling
42
+ * initClient({
43
+ * hydrateRootOptions: {
44
+ * onUncaughtError: (error, errorInfo) => {
45
+ * console.error("Uncaught error:", error);
46
+ * // Send to monitoring service
47
+ * sendToSentry(error, errorInfo);
48
+ * },
49
+ * onCaughtError: (error, errorInfo) => {
50
+ * console.error("Caught error:", error);
51
+ * // Handle errors from error boundaries
52
+ * sendToSentry(error, errorInfo);
53
+ * },
54
+ * },
55
+ * });
56
+ *
57
+ * @example
37
58
  * // With custom React hydration options
38
59
  * initClient({
39
60
  * hydrateRootOptions: {
@@ -105,7 +105,11 @@ export const fetchTransport = (transportContext) => {
105
105
  * making the page interactive. Call this from your client entry point.
106
106
  *
107
107
  * @param transport - Custom transport for server communication (defaults to fetchTransport)
108
- * @param hydrateRootOptions - Options passed to React's hydrateRoot
108
+ * @param hydrateRootOptions - Options passed to React's `hydrateRoot`. Supports all React hydration options including:
109
+ * - `onUncaughtError`: Handler for uncaught errors (async errors, event handler errors).
110
+ * If not provided, defaults to logging errors to console.
111
+ * - `onCaughtError`: Handler for errors caught by error boundaries
112
+ * - `onRecoverableError`: Handler for recoverable errors
109
113
  * @param handleResponse - Custom response handler for navigation errors (navigation GETs)
110
114
  * @param onHydrationUpdate - Callback invoked after a new RSC payload has been committed on the client
111
115
  * @param onActionResponse - Optional hook invoked when an action returns a Response;
@@ -126,6 +130,23 @@ export const fetchTransport = (transportContext) => {
126
130
  * initClient({ handleResponse });
127
131
  *
128
132
  * @example
133
+ * // With error handling
134
+ * initClient({
135
+ * hydrateRootOptions: {
136
+ * onUncaughtError: (error, errorInfo) => {
137
+ * console.error("Uncaught error:", error);
138
+ * // Send to monitoring service
139
+ * sendToSentry(error, errorInfo);
140
+ * },
141
+ * onCaughtError: (error, errorInfo) => {
142
+ * console.error("Caught error:", error);
143
+ * // Handle errors from error boundaries
144
+ * sendToSentry(error, errorInfo);
145
+ * },
146
+ * },
147
+ * });
148
+ *
149
+ * @example
129
150
  * // With custom React hydration options
130
151
  * initClient({
131
152
  * hydrateRootOptions: {
@@ -158,7 +179,7 @@ export const initClient = async ({ transport = fetchTransport, hydrateRootOption
158
179
  };
159
180
  const rootEl = document.getElementById("hydrate-root");
160
181
  if (!rootEl) {
161
- throw new Error('no element with id "hydrate-root"');
182
+ throw new Error('RedwoodSDK: No element with id "hydrate-root" found in the document. This element is required for hydration. Ensure your Document component contains a <div id="hydrate-root">{children}</div>.');
162
183
  }
163
184
  let rscPayload;
164
185
  // context(justinvdm, 18 Jun 2025): We inject the RSC payload
@@ -1,2 +1,2 @@
1
1
  "use strict";
2
- throw new Error("rwsdk: SSR bridge was resolved with 'react-server' condition. This is a bug - the SSR bridge should be intercepted by the esbuild plugin before reaching package.json exports. Please report this issue.");
2
+ throw new Error("RedwoodSDK: SSR bridge was resolved with 'react-server' condition. This is a bug - the SSR bridge should be intercepted by the esbuild plugin before reaching package.json exports. Please report this issue at https://github.com/redwoodjs/sdk/issues.");
@@ -1,2 +1,4 @@
1
1
  "use strict";
2
- throw new Error("rwsdk: 'react-server' is not supported in this environment");
2
+ throw new Error("RedwoodSDK: A client-only module was incorrectly resolved with the 'react-server' condition.\n\n" +
3
+ "This error occurs when modules like 'rwsdk/client', 'rwsdk/__ssr', or 'rwsdk/__ssr_bridge' are being imported in a React Server Components context.\n\n" +
4
+ "For detailed troubleshooting steps, see: https://docs.rwsdk.com/guides/troubleshooting#react-server-components-configuration-errors");
@@ -1,2 +1,2 @@
1
1
  "use strict";
2
- throw new Error("rwsdk: 'react-server' import condition needs to be used in this environment");
2
+ throw new Error("RedwoodSDK: 'react-server' import condition needs to be used in this environment. This code should only run in React Server Components (RSC) context. Check that you're not importing server-only code in client components.");
@@ -1,3 +1,4 @@
1
+ import "server-only";
1
2
  import "./types/worker";
2
3
  export * from "../error";
3
4
  export * from "../lib/types";
@@ -1,3 +1,4 @@
1
+ import "server-only";
1
2
  import "./types/worker";
2
3
  export * from "../error";
3
4
  export * from "../lib/types";
@@ -19,13 +19,13 @@ export function defineLinks(routes) {
19
19
  // Original implementation for route arrays
20
20
  routes.forEach((route) => {
21
21
  if (typeof route !== "string") {
22
- throw new Error(`Invalid route: ${route}. Routes must be strings.`);
22
+ throw new Error(`RedwoodSDK: Invalid route: ${route}. Routes must be string literals. Ensure you're passing an array of route paths.`);
23
23
  }
24
24
  });
25
25
  const link = createLinkFunction();
26
26
  return ((path, params) => {
27
27
  if (!routes.includes(path)) {
28
- throw new Error(`Invalid route: ${path}`);
28
+ throw new Error(`RedwoodSDK: Invalid route: ${path}. This route is not included in the routes array passed to defineLinks(). Check for typos or ensure the route is defined in your router.`);
29
29
  }
30
30
  return link(path, params);
31
31
  });
@@ -36,7 +36,7 @@ function createLinkFunction() {
36
36
  const expectsParams = hasRouteParameters(path);
37
37
  if (!params || Object.keys(params).length === 0) {
38
38
  if (expectsParams) {
39
- throw new Error(`Route ${path} requires an object of parameters`);
39
+ throw new Error(`RedwoodSDK: Route ${path} requires an object of parameters (e.g., link("${path}", { id: "123" })).`);
40
40
  }
41
41
  return path;
42
42
  }
@@ -62,7 +62,7 @@ function interpolate(template, params) {
62
62
  const name = match[1];
63
63
  const value = params[name];
64
64
  if (value === undefined) {
65
- throw new Error(`Missing parameter "${name}" for route ${template}`);
65
+ throw new Error(`RedwoodSDK: Missing parameter "${name}" for route ${template}. Ensure you're providing all required parameters in the params object.`);
66
66
  }
67
67
  result += encodeURIComponent(value);
68
68
  consumed.add(name);
@@ -71,7 +71,7 @@ function interpolate(template, params) {
71
71
  const key = `$${wildcardIndex}`;
72
72
  const value = params[key];
73
73
  if (value === undefined) {
74
- throw new Error(`Missing parameter "${key}" for route ${template}`);
74
+ throw new Error(`RedwoodSDK: Missing parameter "${key}" for route ${template}. Wildcard routes use $0, $1, etc. as parameter keys.`);
75
75
  }
76
76
  result += encodeWildcardValue(value);
77
77
  consumed.add(key);
@@ -82,7 +82,7 @@ function interpolate(template, params) {
82
82
  result += template.slice(lastIndex);
83
83
  for (const key of Object.keys(params)) {
84
84
  if (!consumed.has(key)) {
85
- throw new Error(`Parameter "${key}" is not used by route ${template}`);
85
+ throw new Error(`RedwoodSDK: Parameter "${key}" is not used by route ${template}. Check your params object for typos or remove unused parameters.`);
86
86
  }
87
87
  }
88
88
  TOKEN_REGEX.lastIndex = 0;
@@ -6,6 +6,10 @@ type BivariantRouteHandler<T extends RequestInfo, R> = {
6
6
  bivarianceHack(requestInfo: T): R;
7
7
  }["bivarianceHack"];
8
8
  export type RouteMiddleware<T extends RequestInfo = RequestInfo> = BivariantRouteHandler<T, MaybePromise<React.JSX.Element | Response | void>>;
9
+ export type ExceptHandler<T extends RequestInfo = RequestInfo> = {
10
+ __rwExcept: true;
11
+ handler: (error: unknown, requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>;
12
+ };
9
13
  type RouteFunction<T extends RequestInfo = RequestInfo> = BivariantRouteHandler<T, MaybePromise<Response>>;
10
14
  type RouteComponent<T extends RequestInfo = RequestInfo> = BivariantRouteHandler<T, MaybePromise<React.JSX.Element | Response | void>>;
11
15
  type RouteHandler<T extends RequestInfo = RequestInfo> = RouteFunction<T> | RouteComponent<T> | readonly [...RouteMiddleware<T>[], RouteFunction<T> | RouteComponent<T>];
@@ -22,7 +26,7 @@ export type MethodHandlers<T extends RequestInfo = RequestInfo> = {
22
26
  [method: string]: RouteHandler<T>;
23
27
  };
24
28
  };
25
- export type Route<T extends RequestInfo = RequestInfo> = RouteMiddleware<T> | RouteDefinition<string, T> | readonly Route<T>[];
29
+ export type Route<T extends RequestInfo = RequestInfo> = RouteMiddleware<T> | RouteDefinition<string, T> | ExceptHandler<T> | readonly Route<T>[];
26
30
  type NormalizedRouteDefinition<T extends RequestInfo = RequestInfo> = {
27
31
  path: string;
28
32
  handler: RouteHandler<T> | MethodHandlers<T>;
@@ -36,7 +40,7 @@ type TrimLeadingSlash<S extends string> = S extends `/${infer Rest}` ? TrimLeadi
36
40
  type NormalizePrefix<Prefix extends string> = TrimTrailingSlash<TrimLeadingSlash<Prefix>> extends "" ? "" : `/${TrimTrailingSlash<TrimLeadingSlash<Prefix>>}`;
37
41
  type NormalizePath<Path extends string> = TrimTrailingSlash<Path> extends "/" ? "/" : `/${TrimTrailingSlash<TrimLeadingSlash<Path>>}`;
38
42
  type JoinPaths<Prefix extends string, Path extends string> = NormalizePrefix<Prefix> extends "" ? NormalizePath<Path> : Path extends "/" ? NormalizePrefix<Prefix> : `${NormalizePrefix<Prefix>}${NormalizePath<Path>}`;
39
- type PrefixedRouteValue<Prefix extends string, Value> = Value extends RouteDefinition<infer Path, infer Req> ? RouteDefinition<JoinPaths<Prefix, Path>, Req> : Value extends readonly Route<any>[] ? PrefixedRouteArray<Prefix, Value> : Value;
43
+ type PrefixedRouteValue<Prefix extends string, Value> = Value extends RouteDefinition<infer Path, infer Req> ? RouteDefinition<JoinPaths<Prefix, Path>, Req> : Value extends ExceptHandler<any> ? Value : Value extends readonly Route<any>[] ? PrefixedRouteArray<Prefix, Value> : Value;
40
44
  type PrefixedRouteArray<Prefix extends string, Routes extends readonly Route<any>[]> = Routes extends readonly [] ? [] : Routes extends readonly [infer Head, ...infer Tail] ? readonly [
41
45
  PrefixedRouteValue<Prefix, Head>,
42
46
  ...PrefixedRouteArray<Prefix, Tail extends readonly Route<any>[] ? Tail : []>
@@ -104,24 +108,22 @@ export declare function route<Path extends string, T extends RequestInfo = Reque
104
108
  */
105
109
  export declare function index<T extends RequestInfo = RequestInfo>(handler: RouteHandler<T>): RouteDefinition<"/", T>;
106
110
  /**
107
- * Prefixes a group of routes with a path.
111
+ * Defines an error handler that catches errors from routes, middleware, and RSC actions.
108
112
  *
109
113
  * @example
110
- * // Organize blog routes under /blog
111
- * const blogRoutes = [
112
- * route("/", () => <BlogIndex />),
113
- * route("/post/:id", ({ params }) => <BlogPost id={params.id} />),
114
- * route("/admin", [isAuthenticated, () => <BlogAdmin />]),
115
- * ]
114
+ * // Global error handler
115
+ * except((error, requestInfo) => {
116
+ * console.error(error);
117
+ * return new Response("Internal Server Error", { status: 500 });
118
+ * })
116
119
  *
117
- * // In worker.tsx
118
- * defineApp([
119
- * render(Document, [
120
- * route("/", () => <HomePage />),
121
- * prefix("/blog", blogRoutes),
122
- * ]),
123
- * ])
120
+ * @example
121
+ * // Error handler that returns a React component
122
+ * except((error) => {
123
+ * return <ErrorPage error={error} />;
124
+ * })
124
125
  */
126
+ export declare function except<T extends RequestInfo = RequestInfo>(handler: (error: unknown, requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>): ExceptHandler<T>;
125
127
  export declare function prefix<Prefix extends string, T extends RequestInfo = RequestInfo, Routes extends readonly Route<T>[] = readonly Route<T>[]>(prefixPath: Prefix, routes: Routes): PrefixedRouteArray<Prefix, Routes>;
126
128
  export declare const wrapHandlerToThrowResponses: <T extends RequestInfo = RequestInfo>(handler: RouteFunction<T> | RouteComponent<T>) => RouteHandler<T>;
127
129
  /**
@@ -129,7 +131,7 @@ export declare const wrapHandlerToThrowResponses: <T extends RequestInfo = Reque
129
131
  *
130
132
  * @example
131
133
  * // Define a layout component
132
- * function BlogLayout({ children }: { children: React.ReactNode }) {
134
+ * function BlogLayout({ children }: { children?: React.ReactNode }) {
133
135
  * return (
134
136
  * <div>
135
137
  * <nav>Blog Navigation</nav>