@rpcbase/client 0.290.0 → 0.292.0

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.
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ import { SsrErrorStatePayload } from '../ssrErrorState';
3
+ type RenderErrorExtra = (state: SsrErrorStatePayload) => ReactNode;
4
+ type RouteErrorBoundaryProps = {
5
+ renderErrorExtra?: RenderErrorExtra;
6
+ };
7
+ export declare const RouteErrorBoundary: ({ renderErrorExtra }: RouteErrorBoundaryProps) => import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=RouteErrorBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/components/RouteErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAIjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAGvD,KAAK,gBAAgB,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAA;AAElE,KAAK,uBAAuB,GAAG;IAC7B,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;CACpC,CAAA;AAkDD,eAAO,MAAM,kBAAkB,GAAI,sBAAsB,uBAAuB,4CAU/E,CAAA"}
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ import { SsrErrorStatePayload } from '../ssrErrorState';
3
+ type RenderErrorExtra = (state: SsrErrorStatePayload) => ReactNode;
4
+ type SsrErrorFallbackProps = {
5
+ state: SsrErrorStatePayload;
6
+ renderErrorExtra?: RenderErrorExtra;
7
+ };
8
+ export declare const SsrErrorFallback: ({ state, renderErrorExtra }: SsrErrorFallbackProps) => import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=SsrErrorFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SsrErrorFallback.d.ts","sourceRoot":"","sources":["../../src/components/SsrErrorFallback.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAGvD,KAAK,gBAAgB,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAA;AAElE,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,oBAAoB,CAAA;IAC3B,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;CACpC,CAAA;AAKD,eAAO,MAAM,gBAAgB,GAAI,6BAA6B,qBAAqB,4CAoClF,CAAA"}
package/dist/index.d.ts CHANGED
@@ -4,4 +4,7 @@ export * from './types';
4
4
  export * from './getFeatureFlag';
5
5
  export * from './RootProvider';
6
6
  export * from './hooks';
7
+ export * from './ssrErrorState';
8
+ export * from './components/SsrErrorFallback';
9
+ export * from './components/RouteErrorBoundary';
7
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { StrictMode, useRef, useEffect, useCallback, useState, useSyncExternalStore } from "react";
3
3
  import posthog from "posthog-js";
4
- import { createRoutesFromElements, createBrowserRouter, RouterProvider, useLocation } from "@rpcbase/router";
4
+ import { createRoutesFromElements, createBrowserRouter, RouterProvider, useLocation, useRouteError, isRouteErrorResponse } from "@rpcbase/router";
5
5
  import { hydrateRoot } from "react-dom/client";
6
6
  import { PostHogProvider } from "posthog-js/react/dist/esm/index.js";
7
7
  import useMeasure from "react-use/esm/useMeasure.js";
@@ -77,6 +77,53 @@ const cleanupURL = () => {
77
77
  document.addEventListener("DOMContentLoaded", runCleanup, { once: true });
78
78
  }
79
79
  };
80
+ const SSR_ERROR_STATE_GLOBAL_KEY = "__RPCBASE_SSR_ERROR__";
81
+ const ESCAPED_LT = /</g;
82
+ const ESCAPED_U2028 = /\u2028/g;
83
+ const ESCAPED_U2029 = /\u2029/g;
84
+ const serializeSsrErrorState = (state) => JSON.stringify(state).replace(ESCAPED_LT, "\\u003c").replace(ESCAPED_U2028, "\\u2028").replace(ESCAPED_U2029, "\\u2029");
85
+ const peekClientSsrErrorState = () => {
86
+ if (typeof window === "undefined") {
87
+ return null;
88
+ }
89
+ const globalScope = window;
90
+ const state = globalScope[SSR_ERROR_STATE_GLOBAL_KEY];
91
+ if (state && typeof state === "object") {
92
+ return state;
93
+ }
94
+ return null;
95
+ };
96
+ const consumeClientSsrErrorState = () => {
97
+ const state = peekClientSsrErrorState();
98
+ if (!state) {
99
+ return null;
100
+ }
101
+ if (typeof window !== "undefined") {
102
+ delete window[SSR_ERROR_STATE_GLOBAL_KEY];
103
+ }
104
+ return state;
105
+ };
106
+ const DEFAULT_TITLE = "Something went wrong";
107
+ const DEFAULT_MESSAGE = "We couldn't render this page. Please try again in a few seconds.";
108
+ const SsrErrorFallback = ({ state, renderErrorExtra }) => {
109
+ const {
110
+ statusCode = 500,
111
+ title = DEFAULT_TITLE,
112
+ message = DEFAULT_MESSAGE,
113
+ details
114
+ } = state;
115
+ const extra = renderErrorExtra?.(state);
116
+ return /* @__PURE__ */ jsx("div", { className: "bg-white min-h-screen", children: /* @__PURE__ */ jsxs("main", { className: "mx-auto flex min-h-screen max-w-2xl flex-col items-center justify-center px-6 py-12 text-center sm:py-24", children: [
117
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-rose-600", children: statusCode }),
118
+ /* @__PURE__ */ jsx("h1", { className: "mt-4 text-pretty text-4xl font-semibold tracking-tight text-gray-900 sm:text-5xl", children: title }),
119
+ /* @__PURE__ */ jsx("p", { className: "mt-6 text-lg text-gray-600 sm:text-xl/relaxed", children: message }),
120
+ details ? /* @__PURE__ */ jsxs("div", { className: "mt-10 w-full rounded-2xl bg-gray-900/95 p-5 text-left shadow-2xl", children: [
121
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wide text-gray-400", children: "Debug details" }),
122
+ /* @__PURE__ */ jsx("pre", { className: "mt-3 max-h-64 overflow-auto whitespace-pre-wrap text-sm text-gray-100", children: details })
123
+ ] }) : null,
124
+ extra ? /* @__PURE__ */ jsx("div", { className: "mt-10 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-center", children: extra }) : null
125
+ ] }) });
126
+ };
80
127
  const isProduction = globalThis.__rb_env__.MODE === "production";
81
128
  const showErrorOverlay = (err) => {
82
129
  const ErrorOverlay = customElements.get("vite-error-overlay");
@@ -98,7 +145,30 @@ const handleServerErrors = () => {
98
145
  });
99
146
  }
100
147
  };
148
+ const getRootElement = () => {
149
+ const el = document.getElementById("root");
150
+ if (!el) {
151
+ throw new Error("Root element #root not found");
152
+ }
153
+ return el;
154
+ };
155
+ const hydrateSsrFallbackIfPresent = (rootElement, renderErrorExtra) => {
156
+ const ssrErrorState = peekClientSsrErrorState();
157
+ if (!ssrErrorState) {
158
+ return false;
159
+ }
160
+ consumeClientSsrErrorState();
161
+ hydrateRoot(
162
+ rootElement,
163
+ /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(SsrErrorFallback, { state: ssrErrorState, renderErrorExtra }) })
164
+ );
165
+ return true;
166
+ };
101
167
  const initWithRoutes = async (routesElement, opts) => {
168
+ const rootElement = getRootElement();
169
+ if (hydrateSsrFallbackIfPresent(rootElement, opts?.renderErrorExtra)) {
170
+ return;
171
+ }
102
172
  await initApiClient();
103
173
  cleanupURL();
104
174
  handleServerErrors();
@@ -143,7 +213,7 @@ const initWithRoutes = async (routesElement, opts) => {
143
213
  }
144
214
  } : void 0;
145
215
  hydrateRoot(
146
- document.getElementById("root"),
216
+ rootElement,
147
217
  /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(RouterProvider, { router }) }),
148
218
  hydrationOptions
149
219
  );
@@ -1514,12 +1584,70 @@ const useMediaQuery = (query) => {
1514
1584
  };
1515
1585
  return useSyncExternalStore(subscribe, getSnapshot, () => false);
1516
1586
  };
1587
+ const defaultState = {
1588
+ statusCode: 500,
1589
+ title: "Something went wrong",
1590
+ message: "We couldn't render this route. Please try again shortly."
1591
+ };
1592
+ const toSsrErrorState = (error) => {
1593
+ if (isRouteErrorResponse(error)) {
1594
+ const data = error.data;
1595
+ const message = typeof data === "string" ? data : data?.message || defaultState.message;
1596
+ let details;
1597
+ if (globalThis.__rb_env__.DEV && data && typeof data !== "string") {
1598
+ try {
1599
+ details = JSON.stringify(data, null, 2);
1600
+ } catch {
1601
+ details = void 0;
1602
+ }
1603
+ }
1604
+ return {
1605
+ statusCode: error.status ?? defaultState.statusCode,
1606
+ title: error.statusText || defaultState.title,
1607
+ message,
1608
+ details
1609
+ };
1610
+ }
1611
+ if (error instanceof Error) {
1612
+ return {
1613
+ statusCode: defaultState.statusCode,
1614
+ title: defaultState.title,
1615
+ message: error.message || defaultState.message,
1616
+ details: globalThis.__rb_env__.DEV ? error.stack ?? error.message : void 0
1617
+ };
1618
+ }
1619
+ if (typeof error === "string") {
1620
+ return {
1621
+ statusCode: defaultState.statusCode,
1622
+ title: defaultState.title,
1623
+ message: error
1624
+ };
1625
+ }
1626
+ return defaultState;
1627
+ };
1628
+ const RouteErrorBoundary = ({ renderErrorExtra }) => {
1629
+ const routeError = useRouteError();
1630
+ const state = toSsrErrorState(routeError);
1631
+ return /* @__PURE__ */ jsx(
1632
+ SsrErrorFallback,
1633
+ {
1634
+ state,
1635
+ renderErrorExtra
1636
+ }
1637
+ );
1638
+ };
1517
1639
  export {
1518
1640
  RootProvider,
1641
+ RouteErrorBoundary,
1642
+ SSR_ERROR_STATE_GLOBAL_KEY,
1643
+ SsrErrorFallback,
1519
1644
  apiClient,
1645
+ consumeClientSsrErrorState,
1520
1646
  getFeatureFlag,
1521
1647
  initApiClient,
1522
1648
  initWithRoutes,
1649
+ peekClientSsrErrorState,
1650
+ serializeSsrErrorState,
1523
1651
  useMediaQuery,
1524
1652
  useThrottledMeasure
1525
1653
  };
@@ -1,6 +1,9 @@
1
+ import { ReactNode } from 'react';
1
2
  import { createRoutesFromElements } from '../../router/src';
3
+ import { SsrErrorStatePayload } from './ssrErrorState';
2
4
  type InitWithRoutesOptions = {
3
5
  devThrowsOnHydrationErrors?: boolean;
6
+ renderErrorExtra?: (state: SsrErrorStatePayload) => ReactNode;
4
7
  };
5
8
  type RoutesElement = Parameters<typeof createRoutesFromElements>[0];
6
9
  export declare const initWithRoutes: (routesElement: RoutesElement, opts?: InitWithRoutesOptions) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"initWithRoutes.d.ts","sourceRoot":"","sources":["../src/initWithRoutes.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,wBAAwB,EAAiB,MAAM,iBAAiB,CAAA;AAqC7F,KAAK,qBAAqB,GAAG;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAA;CACrC,CAAA;AAED,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAA;AAEnE,eAAO,MAAM,cAAc,GACzB,eAAe,aAAa,EAC5B,OAAO,qBAAqB,kBA0E7B,CAAA"}
1
+ {"version":3,"file":"initWithRoutes.d.ts","sourceRoot":"","sources":["../src/initWithRoutes.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAE7C,OAAO,EAAsB,wBAAwB,EAAiB,MAAM,iBAAiB,CAAA;AAK7F,OAAO,EAGL,oBAAoB,EACrB,MAAM,iBAAiB,CAAA;AAkCxB,KAAK,qBAAqB,GAAG;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAA;CAC9D,CAAA;AAED,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAA;AA8BnE,eAAO,MAAM,cAAc,GACzB,eAAe,aAAa,EAC5B,OAAO,qBAAqB,kBA+E7B,CAAA"}
@@ -0,0 +1,11 @@
1
+ export declare const SSR_ERROR_STATE_GLOBAL_KEY = "__RPCBASE_SSR_ERROR__";
2
+ export type SsrErrorStatePayload = {
3
+ statusCode?: number;
4
+ title?: string;
5
+ message?: string;
6
+ details?: string;
7
+ };
8
+ export declare const serializeSsrErrorState: (state: SsrErrorStatePayload) => string;
9
+ export declare const peekClientSsrErrorState: () => SsrErrorStatePayload | null;
10
+ export declare const consumeClientSsrErrorState: () => SsrErrorStatePayload | null;
11
+ //# sourceMappingURL=ssrErrorState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrErrorState.d.ts","sourceRoot":"","sources":["../src/ssrErrorState.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AAEjE,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAMD,eAAO,MAAM,sBAAsB,GAAI,OAAO,oBAAoB,KAAG,MAI/B,CAAA;AAEtC,eAAO,MAAM,uBAAuB,QAAO,oBAAoB,GAAG,IAUjE,CAAA;AAED,eAAO,MAAM,0BAA0B,QAAO,oBAAoB,GAAG,IASpE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/client",
3
- "version": "0.290.0",
3
+ "version": "0.292.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"