tempest-react-sdk 0.42.1 → 0.43.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.
package/README.md CHANGED
@@ -159,28 +159,30 @@ Requires React `>=18` and Node `>=22.12` to build.
159
159
 
160
160
  ### Peer & bundled dependencies
161
161
 
162
- Only **react** and **react-dom** are peer dependencies — those must come from the host app so a single React copy lives in the tree.
162
+ **react**, **react-dom** and **react-router** are peer dependencies — those must come from the host app, because a second copy is not just wasted bytes, it is a second _instance_ of a React context and it breaks at runtime.
163
163
 
164
- Everything else (`zod`, `zustand`, `dexie`, `react-hook-form`, `@tanstack/react-query`, `react-router`, `lucide-react`) is a **direct dependency** of the SDK, installed automatically by `npm install tempest-react-sdk`. You never need to install them manually.
164
+ Everything else (`zod`, `zustand`, `dexie`, `react-hook-form`, `@tanstack/react-query`, `lucide-react`) is a **direct dependency** of the SDK, installed automatically by `npm install tempest-react-sdk`. You never need to install them manually.
165
165
 
166
166
  | Package | Status | Used by |
167
167
  | ------------------------------------- | ------------------- | ----------------------------------------------------------------------- |
168
168
  | `react`, `react-dom` (`^18 \|\| ^19`) | **Peer (required)** | Everything |
169
+ | `react-router` (`^7 \|\| ^8`) | **Peer (required)** | `AppRouter`, `defineRoutes`, `RouteGuard`, routing re-exports |
169
170
  | `@tanstack/react-query` (`^5`) | Direct dep (auto) | `QueryProvider`, `createQueryKeys`, `AppProviders` |
170
171
  | `zod` (`^3.23 \|\| ^4`) | Direct dep (auto) | `parseResponse`, `validateForm`, `zodResolver`, `useZodForm` |
171
172
  | `zustand` (`^4 \|\| ^5`) | Direct dep (auto) | `createAuthStore`, `createStore`, `createSelectors` |
172
- | `react-router` (`^7`) | Direct dep (auto) | `AppRouter`, `defineRoutes`, `RouteGuard`, routing re-exports |
173
173
  | `dexie` (`^4.4`) | Direct dep (auto) | `createOfflineStore` |
174
174
  | `react-hook-form` (`^7.76`) | Direct dep (auto) | `zodResolver`, `useZodForm`, masked inputs |
175
- | `lucide-react` (`>=0.400`) | Direct dep (auto) | Component icons (`leftIcon`/`rightIcon` on `Input`, `Button`, etc.) |
175
+ | `lucide-react` (`^1.31`) | Direct dep (auto) | Component icons (`leftIcon`/`rightIcon` on `Input`, `Button`, etc.) |
176
176
  | `vite`, `@vitejs/plugin-react` | **Optional peer** | `createViteConfig` (`tempest-react-sdk/vite`) — already in any Vite app |
177
177
 
178
178
  The minimum install is just:
179
179
 
180
180
  ```bash
181
- npm install tempest-react-sdk react react-dom
181
+ npm install tempest-react-sdk react react-dom react-router
182
182
  ```
183
183
 
184
+ **Why `react-router` is a peer and not a bundled dep.** It holds React context. A copy nested under `tempest-react-sdk/node_modules` is a _different_ `<Router>` context than the one your app renders, so any SDK hook reaching for it throws `useNavigate() may be used only in the context of a <Router>` — a runtime crash, not a size regression. That is the same reason `react` itself is a peer, and it is why `react-router` is the one exception to the "everything is a direct dep" rule. The `^7 || ^8` range means an app on either major installs one copy and the SDK adapts to it: the re-exported surface is identical across both, and both ship the DOM bindings inside `react-router` (there is no separate `react-router-dom`).
185
+
184
186
  **Bundle impact**: every bundled dep is externalised in the SDK's Rollup config, and `dist/` ships with the module graph preserved (one file per source module), so your bundler drops what you don't import instead of inheriting one opaque blob — importing `cn` alone costs **153 B** brotli, a full app shell around **6.8 KB**. Your app's bundler (Vite / webpack / Rspack) resolves the deps from `node_modules` and tree-shakes them the same way — if you never call `createOfflineStore`, Dexie never enters your final bundle.
185
187
 
186
188
  **Version conflicts**: if your app already pins (say) `zod@3.20`, npm dedupes when the range is compatible. If ranges diverge you get two copies — pin a single version in your own `package.json` to force one, or open an issue if the SDK's range is too tight.
@@ -67,7 +67,16 @@ async function isEmptyDir(dir) {
67
67
  return entries.filter((e) => e !== ".git").length === 0;
68
68
  }
69
69
 
70
- /** Read the SDK's own version so the generated app pins a matching range. */
70
+ /**
71
+ * Read the SDK's own version so the generated app pins a matching range.
72
+ *
73
+ * This is why `template/package.json` deliberately does **not** list
74
+ * `tempest-react-sdk` in its dependencies: every write path below stamps the
75
+ * live version over whatever is there, so a literal range in the template is a
76
+ * dead value that only drifts (it sat at `^0.27.0` fifteen minors after the
77
+ * fact) and misleads whoever reads the file. `test/scaffold-template.test.ts`
78
+ * keeps it out.
79
+ */
71
80
  async function readSdkVersion() {
72
81
  try {
73
82
  const pkg = JSON.parse(await readFile(join(PKG_ROOT, "package.json"), "utf8"));
@@ -1 +1 @@
1
- {"version":3,"file":"AppRouter.cjs","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":"6JAoBA,SAAgB,EAAa,EAAoD,CAC7E,OAAO,CACX,CAEA,IAAM,EAAY,IAAI,QAEtB,SAAS,EAAY,EAAmD,CACpE,IAAI,EAAO,EAAU,IAAI,CAAK,EAK9B,OAJK,IACD,EAAO,EAAA,cAAc,EAAM,IAAK,EAChC,EAAU,IAAI,EAAO,CAAI,GAEtB,CACX,CAEA,SAAS,EAAe,CACpB,QACA,aACA,YAKD,CACC,IAAM,EAAU,OAAO,GAAU,WAAa,EAAM,EAAI,EACxD,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,KAAM,EAAqB,aAClC,UACO,CAAA,CAEpB,CAEA,SAAS,EAAgB,EAAyC,CAC9D,OAAO,EAAO,KAAK,EAAO,IAAM,CAC5B,IAAI,EAAqB,EAAM,QAC/B,GAAI,EAAM,KAAM,CACZ,IAAM,EAAgB,EAAY,CAAK,EACvC,GAAU,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,CAAA,CAC9B,CAaA,OAZI,EAAM,QAAU,IAAA,KAChB,GACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,MAAO,EAAM,MAAO,WAAY,EAAM,WACjD,SAAA,CACW,CAAA,GAIpB,EAAM,OACC,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAA0B,MAAA,GAAe,SAAU,EAAvC,SAAS,GAA8B,GAI1D,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAEI,KAAM,EAAM,KACH,UACT,cAAe,EAAM,cAEpB,SAAA,EAAM,SAAW,EAAgB,EAAM,QAAQ,EAAI,IACjD,EANE,EAAM,MAAQ,SAAS,GAMzB,CAEf,CAAC,CACL,CAEA,IAAM,EAAoD,CACtD,QAAS,EAAA,cACT,KAAM,EAAA,WACN,OAAQ,EAAA,YACZ,EAwBA,SAAgB,EAAU,CACtB,SACA,SAAS,UACT,WACA,iBACA,WAAW,MACI,CACf,IAAM,GAAA,EAAO,EAAA,QAAA,KAAc,EAAgB,CAAM,EAAG,CAAC,CAAM,CAAC,EACtD,EAAS,EAAQ,GAGvB,OACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,GAHQ,IAAW,SAAW,CAAE,WAAU,gBAAe,EAAI,CAAE,UAAS,EAI5E,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAoB,WAChB,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAa,CAAA,CAChB,CAAA,CACN,CAAA,CAEhB"}
1
+ {"version":3,"file":"AppRouter.cjs","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7 or v8, declarative mode) from a\n * {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":"6JAoBA,SAAgB,EAAa,EAAoD,CAC7E,OAAO,CACX,CAEA,IAAM,EAAY,IAAI,QAEtB,SAAS,EAAY,EAAmD,CACpE,IAAI,EAAO,EAAU,IAAI,CAAK,EAK9B,OAJK,IACD,EAAO,EAAA,cAAc,EAAM,IAAK,EAChC,EAAU,IAAI,EAAO,CAAI,GAEtB,CACX,CAEA,SAAS,EAAe,CACpB,QACA,aACA,YAKD,CACC,IAAM,EAAU,OAAO,GAAU,WAAa,EAAM,EAAI,EACxD,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,KAAM,EAAqB,aAClC,UACO,CAAA,CAEpB,CAEA,SAAS,EAAgB,EAAyC,CAC9D,OAAO,EAAO,KAAK,EAAO,IAAM,CAC5B,IAAI,EAAqB,EAAM,QAC/B,GAAI,EAAM,KAAM,CACZ,IAAM,EAAgB,EAAY,CAAK,EACvC,GAAU,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,CAAA,CAC9B,CAaA,OAZI,EAAM,QAAU,IAAA,KAChB,GACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAgB,MAAO,EAAM,MAAO,WAAY,EAAM,WACjD,SAAA,CACW,CAAA,GAIpB,EAAM,OACC,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAA0B,MAAA,GAAe,SAAU,EAAvC,SAAS,GAA8B,GAI1D,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAEI,KAAM,EAAM,KACH,UACT,cAAe,EAAM,cAEpB,SAAA,EAAM,SAAW,EAAgB,EAAM,QAAQ,EAAI,IACjD,EANE,EAAM,MAAQ,SAAS,GAMzB,CAEf,CAAC,CACL,CAEA,IAAM,EAAoD,CACtD,QAAS,EAAA,cACT,KAAM,EAAA,WACN,OAAQ,EAAA,YACZ,EAyBA,SAAgB,EAAU,CACtB,SACA,SAAS,UACT,WACA,iBACA,WAAW,MACI,CACf,IAAM,GAAA,EAAO,EAAA,QAAA,KAAc,EAAgB,CAAM,EAAG,CAAC,CAAM,CAAC,EACtD,EAAS,EAAQ,GAGvB,OACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,GAHQ,IAAW,SAAW,CAAE,WAAU,gBAAe,EAAI,CAAE,UAAS,EAI5E,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAoB,WAChB,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAA,SAAS,CAAa,CAAA,CAChB,CAAA,CACN,CAAA,CAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"AppRouter.js","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":";;;;;;AAoBA,SAAgB,EAAa,GAAoD;CAC7E,OAAO;AACX;AAEA,IAAM,oBAAY,IAAI,QAAoD;AAE1E,SAAS,EAAY,GAAmD;CACpE,IAAI,IAAO,EAAU,IAAI,CAAK;CAK9B,OAJK,MACD,IAAO,EAAc,EAAM,IAAK,GAChC,EAAU,IAAI,GAAO,CAAI,IAEtB;AACX;AAEA,SAAS,EAAe,EACpB,UACA,eACA,eAKD;CACC,IAAM,IAAU,OAAO,KAAU,aAAa,EAAM,IAAI;CACxD,OACI,kBAAC,GAAD;EAAY,MAAM;EAAqB;EAClC;CACO,CAAA;AAEpB;AAEA,SAAS,EAAgB,GAAyC;CAC9D,OAAO,EAAO,KAAK,GAAO,MAAM;EAC5B,IAAI,IAAqB,EAAM;EAC/B,IAAI,EAAM,MAAM;GACZ,IAAM,IAAgB,EAAY,CAAK;GACvC,IAAU,kBAAC,GAAD,CAAgB,CAAA;EAC9B;EAaA,OAZI,EAAM,UAAU,KAAA,MAChB,IACI,kBAAC,GAAD;GAAgB,OAAO,EAAM;GAAO,YAAY,EAAM;GACjD,UAAA;EACW,CAAA,IAIpB,EAAM,QACC,kBAAC,GAAD;GAA0B,OAAA;GAAe;EAAU,GAAvC,SAAS,GAA8B,IAI1D,kBAAC,GAAD;GAEI,MAAM,EAAM;GACH;GACT,eAAe,EAAM;GAEpB,UAAA,EAAM,WAAW,EAAgB,EAAM,QAAQ,IAAI;EACjD,GANE,EAAM,QAAQ,SAAS,GAMzB;CAEf,CAAC;AACL;AAEA,IAAM,IAAoD;CACtD,SAAS;CACT,MAAM;CACN,QAAQ;AACZ;AAwBA,SAAgB,EAAU,EACtB,WACA,YAAS,WACT,aACA,mBACA,cAAW,QACI;CACf,IAAM,IAAO,QAAc,EAAgB,CAAM,GAAG,CAAC,CAAM,CAAC,GACtD,IAAS,EAAQ;CAGvB,OACI,kBAAC,GAAD;EAAQ,GAHQ,MAAW,WAAW;GAAE;GAAU;EAAe,IAAI,EAAE,YAAS;EAI5E,UAAA,kBAAC,GAAD;GAAoB;GAChB,UAAA,kBAAC,GAAD,EAAA,UAAS,EAAa,CAAA;EAChB,CAAA;CACN,CAAA;AAEhB"}
1
+ {"version":3,"file":"AppRouter.js","names":[],"sources":["../../src/router/AppRouter.tsx"],"sourcesContent":["import { Suspense, useMemo } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { BrowserRouter, HashRouter, MemoryRouter, Routes, Route } from \"react-router\";\nimport { lazyWithRetry } from \"../auth/lazy-with-retry\";\nimport { RouteGuard } from \"./RouteGuard\";\nimport type { RouterKind, TempestRouteObject } from \"./types\";\n\n/**\n * Identity helper that types a declarative route tree. Use it so editors give\n * autocomplete and type-checking on every node; the array is returned as-is.\n *\n * @example\n * export const routes = defineRoutes([\n * { path: \"/\", element: <Layout />, children: [\n * { index: true, lazy: () => import(\"./pages/Home\") },\n * { path: \"settings\", lazy: () => import(\"./pages/Settings\"),\n * guard: () => useAuth.getState().isAuthenticated, redirectTo: \"/login\" },\n * ] },\n * ]);\n */\nexport function defineRoutes(routes: TempestRouteObject[]): TempestRouteObject[] {\n return routes;\n}\n\nconst lazyCache = new WeakMap<TempestRouteObject, ComponentType<unknown>>();\n\nfunction resolveLazy(route: TempestRouteObject): ComponentType<unknown> {\n let comp = lazyCache.get(route);\n if (!comp) {\n comp = lazyWithRetry(route.lazy!);\n lazyCache.set(route, comp);\n }\n return comp;\n}\n\nfunction GuardedElement({\n guard,\n redirectTo,\n children,\n}: {\n guard: boolean | (() => boolean);\n redirectTo?: string;\n children: ReactNode;\n}) {\n const allowed = typeof guard === \"function\" ? guard() : guard;\n return (\n <RouteGuard when={allowed} redirectTo={redirectTo}>\n {children}\n </RouteGuard>\n );\n}\n\nfunction toRouteElements(routes: TempestRouteObject[]): ReactNode {\n return routes.map((route, i) => {\n let element: ReactNode = route.element;\n if (route.lazy) {\n const LazyComponent = resolveLazy(route);\n element = <LazyComponent />;\n }\n if (route.guard !== undefined) {\n element = (\n <GuardedElement guard={route.guard} redirectTo={route.redirectTo}>\n {element}\n </GuardedElement>\n );\n }\n\n if (route.index) {\n return <Route key={`index-${i}`} index element={element} />;\n }\n\n return (\n <Route\n key={route.path ?? `route-${i}`}\n path={route.path}\n element={element}\n caseSensitive={route.caseSensitive}\n >\n {route.children ? toRouteElements(route.children) : null}\n </Route>\n );\n });\n}\n\nconst ROUTERS: Record<RouterKind, typeof BrowserRouter> = {\n browser: BrowserRouter,\n hash: HashRouter,\n memory: MemoryRouter,\n};\n\nexport interface AppRouterProps {\n /** Declarative route tree, ideally built with {@link defineRoutes}. */\n routes: TempestRouteObject[];\n /** History strategy (default: `\"browser\"`). */\n router?: RouterKind;\n /** App-wide base path forwarded to the underlying router. */\n basename?: string;\n /** Initial URLs for the `\"memory\"` router (tests / non-DOM hosts). */\n initialEntries?: string[];\n /** Suspense fallback shown while a `lazy` route chunk loads. */\n fallback?: ReactNode;\n}\n\n/**\n * Render a full React Router (v7 or v8, declarative mode) from a\n * {@link defineRoutes}\n * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary\n * for `lazy` routes, and applies per-route `guard` redirects. This is the\n * single entry point apps mount at their root.\n *\n * @example\n * <AppRouter routes={routes} fallback={<Spinner />} />\n */\nexport function AppRouter({\n routes,\n router = \"browser\",\n basename,\n initialEntries,\n fallback = null,\n}: AppRouterProps) {\n const tree = useMemo(() => toRouteElements(routes), [routes]);\n const Router = ROUTERS[router];\n const routerProps = router === \"memory\" ? { basename, initialEntries } : { basename };\n\n return (\n <Router {...routerProps}>\n <Suspense fallback={fallback}>\n <Routes>{tree}</Routes>\n </Suspense>\n </Router>\n );\n}\n"],"mappings":";;;;;;AAoBA,SAAgB,EAAa,GAAoD;CAC7E,OAAO;AACX;AAEA,IAAM,oBAAY,IAAI,QAAoD;AAE1E,SAAS,EAAY,GAAmD;CACpE,IAAI,IAAO,EAAU,IAAI,CAAK;CAK9B,OAJK,MACD,IAAO,EAAc,EAAM,IAAK,GAChC,EAAU,IAAI,GAAO,CAAI,IAEtB;AACX;AAEA,SAAS,EAAe,EACpB,UACA,eACA,eAKD;CACC,IAAM,IAAU,OAAO,KAAU,aAAa,EAAM,IAAI;CACxD,OACI,kBAAC,GAAD;EAAY,MAAM;EAAqB;EAClC;CACO,CAAA;AAEpB;AAEA,SAAS,EAAgB,GAAyC;CAC9D,OAAO,EAAO,KAAK,GAAO,MAAM;EAC5B,IAAI,IAAqB,EAAM;EAC/B,IAAI,EAAM,MAAM;GACZ,IAAM,IAAgB,EAAY,CAAK;GACvC,IAAU,kBAAC,GAAD,CAAgB,CAAA;EAC9B;EAaA,OAZI,EAAM,UAAU,KAAA,MAChB,IACI,kBAAC,GAAD;GAAgB,OAAO,EAAM;GAAO,YAAY,EAAM;GACjD,UAAA;EACW,CAAA,IAIpB,EAAM,QACC,kBAAC,GAAD;GAA0B,OAAA;GAAe;EAAU,GAAvC,SAAS,GAA8B,IAI1D,kBAAC,GAAD;GAEI,MAAM,EAAM;GACH;GACT,eAAe,EAAM;GAEpB,UAAA,EAAM,WAAW,EAAgB,EAAM,QAAQ,IAAI;EACjD,GANE,EAAM,QAAQ,SAAS,GAMzB;CAEf,CAAC;AACL;AAEA,IAAM,IAAoD;CACtD,SAAS;CACT,MAAM;CACN,QAAQ;AACZ;AAyBA,SAAgB,EAAU,EACtB,WACA,YAAS,WACT,aACA,mBACA,cAAW,QACI;CACf,IAAM,IAAO,QAAc,EAAgB,CAAM,GAAG,CAAC,CAAM,CAAC,GACtD,IAAS,EAAQ;CAGvB,OACI,kBAAC,GAAD;EAAQ,GAHQ,MAAW,WAAW;GAAE;GAAU;EAAe,IAAI,EAAE,YAAS;EAI5E,UAAA,kBAAC,GAAD;GAAoB;GAChB,UAAA,kBAAC,GAAD,EAAA,UAAS,EAAa,CAAA;EAChB,CAAA;CACN,CAAA;AAEhB"}
@@ -779,7 +779,8 @@ export declare interface AppProvidersProps {
779
779
  }
780
780
 
781
781
  /**
782
- * Render a full React Router (v7, declarative mode) from a {@link defineRoutes}
782
+ * Render a full React Router (v7 or v8, declarative mode) from a
783
+ * {@link defineRoutes}
783
784
  * tree: picks the history strategy, wraps everything in a `<Suspense>` boundary
784
785
  * for `lazy` routes, and applies per-route `guard` redirects. This is the
785
786
  * single entry point apps mount at their root.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tempest-react-sdk",
3
- "version": "0.42.1",
3
+ "version": "0.43.0",
4
4
  "description": "SDK público da Tempest com componentes, hooks e integrações para projetos React.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -138,7 +138,6 @@
138
138
  "fflate": "^0.8.3",
139
139
  "lucide-react": "^1.31.0",
140
140
  "react-hook-form": "^7.76.0",
141
- "react-router": "^8.3.0",
142
141
  "zod": "^3.23.0 || ^4.0.0",
143
142
  "zustand": "^4.0.0 || ^5.0.0"
144
143
  },
@@ -150,6 +149,7 @@
150
149
  "onnxruntime-web": ">=1.17.0",
151
150
  "react": "^18.0.0 || ^19.0.0",
152
151
  "react-dom": "^18.0.0 || ^19.0.0",
152
+ "react-router": "^7.0.0 || ^8.0.0",
153
153
  "recharts": "^2.0.0 || ^3.0.0",
154
154
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
155
155
  },
@@ -212,6 +212,7 @@
212
212
  "react": "^19.1.0",
213
213
  "react-dom": "^19.1.0",
214
214
  "react-hook-form": "^7.76.0",
215
+ "react-router": "^8.3.0",
215
216
  "recharts": "^3.9.0",
216
217
  "size-limit": "^13.0.1",
217
218
  "typescript": "~6.0.3",
@@ -17,7 +17,7 @@
17
17
  "dependencies": {
18
18
  "react": "^19.2.8",
19
19
  "react-dom": "^19.2.8",
20
- "tempest-react-sdk": "^0.27.0"
20
+ "react-router": "^8.3.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@eslint/js": "^10.0.1",