@igstack/app-catalog-frontend-core 0.6.1 → 0.6.2

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,5 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { use, createContext, useState, useEffect, useMemo } from "react";
2
+ import { useState, useEffect, useMemo, createContext, use } from "react";
3
3
  const AuthContext = createContext(void 0);
4
4
  const STORAGE_KEY = "ac_auth_user";
5
5
  function AuthProvider({ children }) {
@@ -1,5 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { use, createContext, useState, useMemo } from "react";
2
+ import { useState, useMemo, createContext, use } from "react";
3
3
  const AuthModalContext = createContext(
4
4
  void 0
5
5
  );
@@ -3,11 +3,17 @@ import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
3
3
  import { RootErrorPage } from "../ui/components/error/RootErrorPage.js";
4
4
  import { NotFoundError } from "../ui/error/NotFoundError.js";
5
5
  import { LoadingScreen } from "../ui/layout/LoadingScreen.js";
6
+ import { TopLevelProvidersForErrors } from "../ui/layout/TopLevelProvidersForErrors.js";
6
7
  const Route = createRootRouteWithContext()({
7
8
  component: RootRoute,
8
9
  errorComponent: RootErrorPage,
9
- pendingComponent: () => /* @__PURE__ */ jsx(LoadingScreen, { label: "root pending" }),
10
- notFoundComponent: () => /* @__PURE__ */ jsx(NotFoundError, {}),
10
+ // These fallbacks render MainLayout Header → useAuth(), so they must be
11
+ // wrapped in the providers (Auth + AuthModal + Theme + Tooltip). The router
12
+ // renders pending/notFound OUTSIDE the route-component subtree, i.e. outside
13
+ // the app's TopLevelProviders — without this wrap they throw
14
+ // "useAuth must be used within AuthProvider" (see #6/#7).
15
+ pendingComponent: () => /* @__PURE__ */ jsx(TopLevelProvidersForErrors, { children: /* @__PURE__ */ jsx(LoadingScreen, { label: "root pending" }) }),
16
+ notFoundComponent: () => /* @__PURE__ */ jsx(TopLevelProvidersForErrors, { children: /* @__PURE__ */ jsx(NotFoundError, {}) }),
11
17
  wrapInSuspense: true
12
18
  });
13
19
  function RootRoute() {
@@ -1 +1 @@
1
- {"version":3,"file":"__root.js","sources":["../../../src/routes/__root.tsx"],"sourcesContent":["import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\nimport type { AcRouterContext } from '~/types/types'\nimport { RootErrorPage } from '~/ui/components/error/RootErrorPage'\nimport { NotFoundError } from '~/ui/error/NotFoundError'\nimport { LoadingScreen } from '~/ui/layout/LoadingScreen'\n\nexport const Route = createRootRouteWithContext<AcRouterContext>()({\n component: RootRoute,\n errorComponent: RootErrorPage,\n pendingComponent: () => <LoadingScreen label=\"root pending\" />,\n notFoundComponent: () => <NotFoundError />,\n wrapInSuspense: true,\n})\n\nfunction RootRoute() {\n return (\n <div className=\"min-h-screen bg-base-200\">\n <Outlet />\n {import.meta.env.MODE === 'dev' ? <TanStackRouterDevtools /> : null}\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;AAOO,MAAM,QAAQ,6BAA8C;AAAA,EACjE,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,kBAAkB,MAAM,oBAAC,eAAA,EAAc,OAAM,gBAAe;AAAA,EAC5D,mBAAmB,MAAM,oBAAC,eAAA,EAAc;AAAA,EACxC,gBAAgB;AAClB,CAAC;AAED,SAAS,YAAY;AACnB,SACE,qBAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAO;AAAA,IACuD;AAAA,EAAA,GACjE;AAEJ;"}
1
+ {"version":3,"file":"__root.js","sources":["../../../src/routes/__root.tsx"],"sourcesContent":["import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\nimport type { AcRouterContext } from '~/types/types'\nimport { RootErrorPage } from '~/ui/components/error/RootErrorPage'\nimport { NotFoundError } from '~/ui/error/NotFoundError'\nimport { LoadingScreen } from '~/ui/layout/LoadingScreen'\nimport { TopLevelProvidersForErrors } from '~/ui/layout/TopLevelProvidersForErrors'\n\nexport const Route = createRootRouteWithContext<AcRouterContext>()({\n component: RootRoute,\n errorComponent: RootErrorPage,\n // These fallbacks render MainLayout → Header → useAuth(), so they must be\n // wrapped in the providers (Auth + AuthModal + Theme + Tooltip). The router\n // renders pending/notFound OUTSIDE the route-component subtree, i.e. outside\n // the app's TopLevelProviders — without this wrap they throw\n // \"useAuth must be used within AuthProvider\" (see #6/#7).\n pendingComponent: () => (\n <TopLevelProvidersForErrors>\n <LoadingScreen label=\"root pending\" />\n </TopLevelProvidersForErrors>\n ),\n notFoundComponent: () => (\n <TopLevelProvidersForErrors>\n <NotFoundError />\n </TopLevelProvidersForErrors>\n ),\n wrapInSuspense: true,\n})\n\nfunction RootRoute() {\n return (\n <div className=\"min-h-screen bg-base-200\">\n <Outlet />\n {import.meta.env.MODE === 'dev' ? <TanStackRouterDevtools /> : null}\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;AAQO,MAAM,QAAQ,6BAA8C;AAAA,EACjE,WAAW;AAAA,EACX,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhB,kBAAkB,MAChB,oBAAC,4BAAA,EACC,8BAAC,eAAA,EAAc,OAAM,eAAA,CAAe,GACtC;AAAA,EAEF,mBAAmB,MACjB,oBAAC,4BAAA,EACC,UAAA,oBAAC,iBAAc,GACjB;AAAA,EAEF,gBAAgB;AAClB,CAAC;AAED,SAAS,YAAY;AACnB,SACE,qBAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAO;AAAA,IACuD;AAAA,EAAA,GACjE;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igstack/app-catalog-frontend-core",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Frontend core library for App Catalog",
5
5
  "homepage": "https://github.com/lislon/app-catalog",
6
6
  "repository": {
@@ -134,8 +134,8 @@
134
134
  "vite-plugin-static-copy": "^3.1.4",
135
135
  "vite-plugin-svgr": "^4.2.0",
136
136
  "vitest": "^4.1.2",
137
- "@igstack/app-catalog-backend-core": "0.6.1",
138
- "@igstack/app-catalog-shared-core": "0.6.1"
137
+ "@igstack/app-catalog-backend-core": "0.6.2",
138
+ "@igstack/app-catalog-shared-core": "0.6.2"
139
139
  },
140
140
  "peerDependencies": {
141
141
  "react": "19.1.2",
@@ -144,7 +144,7 @@
144
144
  "vite": "^6.3.5",
145
145
  "vite-plugin-svgr": "^4.2.0"
146
146
  },
147
- "gitHead": "e83dc91aa65e1699c3fe578db7a62d81c4b27857",
147
+ "gitHead": "dcc2b0acf435e115ec42861ec79064c6394b1ada",
148
148
  "scripts": {
149
149
  "build": "vite build",
150
150
  "build:lenient": "vite build --mode lenient",
@@ -174,7 +174,14 @@ export async function given(
174
174
  const hasTanstackError = document.body.textContent.includes(
175
175
  'Something went wrong',
176
176
  )
177
- if (!hasAlert && !hasTable && !hasSearchbox && !hasTanstackError) {
177
+ const hasNotFound = document.body.textContent.includes('404 Not Found')
178
+ if (
179
+ !hasAlert &&
180
+ !hasTable &&
181
+ !hasSearchbox &&
182
+ !hasTanstackError &&
183
+ !hasNotFound
184
+ ) {
178
185
  throw new Error('Still loading...')
179
186
  }
180
187
  },
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { screen, waitFor } from '@testing-library/react'
3
+ import '@testing-library/jest-dom/vitest'
4
+
5
+ import { given } from './harness/given'
6
+ import { magazine } from './mock-backend/magazines'
7
+
8
+ /**
9
+ * Regression for #7 (root cause in #6): the root route's `notFoundComponent`
10
+ * (`NotFoundError`) renders `MainLayout → Header → useAuth()`. Before the fix
11
+ * the fallback rendered OUTSIDE `AuthProvider`, so any unknown URL threw
12
+ * `useAuth must be used within AuthProvider` and the app showed its "Ooops!"
13
+ * error page instead of a clean 404.
14
+ */
15
+ describe('Unknown URL renders NotFound without a useAuth error', () => {
16
+ it('shows the 404 page (not the useAuth error) for a bogus URL', async () => {
17
+ const { ui } = await given(magazine.full(), {
18
+ initialRoute: '/nonexistent-xyz',
19
+ })
20
+
21
+ // The clean NotFound UI is rendered.
22
+ await waitFor(() => {
23
+ expect(screen.getByText('404 Not Found')).toBeInTheDocument()
24
+ })
25
+
26
+ // And crucially: NO useAuth error surfaced. The header (which calls
27
+ // useAuth) must render inside a provider, so the global error page must
28
+ // not appear at all.
29
+ expect(
30
+ screen.queryByText(/useAuth must be used within AuthProvider/),
31
+ ).not.toBeInTheDocument()
32
+ expect(() => ui.globalError()).toThrow(/No global error found/)
33
+ })
34
+ })
@@ -4,12 +4,26 @@ import type { AcRouterContext } from '~/types/types'
4
4
  import { RootErrorPage } from '~/ui/components/error/RootErrorPage'
5
5
  import { NotFoundError } from '~/ui/error/NotFoundError'
6
6
  import { LoadingScreen } from '~/ui/layout/LoadingScreen'
7
+ import { TopLevelProvidersForErrors } from '~/ui/layout/TopLevelProvidersForErrors'
7
8
 
8
9
  export const Route = createRootRouteWithContext<AcRouterContext>()({
9
10
  component: RootRoute,
10
11
  errorComponent: RootErrorPage,
11
- pendingComponent: () => <LoadingScreen label="root pending" />,
12
- notFoundComponent: () => <NotFoundError />,
12
+ // These fallbacks render MainLayout → Header → useAuth(), so they must be
13
+ // wrapped in the providers (Auth + AuthModal + Theme + Tooltip). The router
14
+ // renders pending/notFound OUTSIDE the route-component subtree, i.e. outside
15
+ // the app's TopLevelProviders — without this wrap they throw
16
+ // "useAuth must be used within AuthProvider" (see #6/#7).
17
+ pendingComponent: () => (
18
+ <TopLevelProvidersForErrors>
19
+ <LoadingScreen label="root pending" />
20
+ </TopLevelProvidersForErrors>
21
+ ),
22
+ notFoundComponent: () => (
23
+ <TopLevelProvidersForErrors>
24
+ <NotFoundError />
25
+ </TopLevelProvidersForErrors>
26
+ ),
13
27
  wrapInSuspense: true,
14
28
  })
15
29