@tristan2828/ui-foundation 3.27.0 → 3.27.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.
@@ -86,9 +86,16 @@ export function AuthProvider({ children }) {
86
86
  async function login(email, password) {
87
87
  startSession(queryClient, await loginRequest({ email, password }));
88
88
  }
89
+ // Logged out on screen even when the request fails (network down): the
90
+ // person asked to leave, and a shell that still shows them signed in is
91
+ // worse. Still rejects, so the caller knows the server didn't confirm.
89
92
  async function logout() {
90
- await logoutRequest();
91
- endSession(queryClient);
93
+ try {
94
+ await logoutRequest();
95
+ }
96
+ finally {
97
+ endSession(queryClient);
98
+ }
92
99
  }
93
100
  async function register(email, name, password) {
94
101
  startSession(queryClient, await registerRequest({ email, name, password }));
@@ -71,6 +71,9 @@ export function AppShell({ title, nav, sidebarExtra, defaultSidebarOpen = true }
71
71
  return (_jsx(SidebarMenuItem, { children: _jsxs(SidebarMenuButton, { isActive: isActive, tooltip: label, render: _jsx(NavLink, { to: to, end: end }), children: [_jsx(Icon, {}), _jsx("span", { children: label })] }) }, to));
72
72
  }) }) })] }), sidebarExtra] }), _jsx(SidebarFooter, { children: _jsxs("div", { className: "flex items-center justify-between gap-2 px-2 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0", children: [_jsx("span", { className: "truncate type-caption text-sidebar-foreground/70 group-data-[collapsible=icon]:hidden", children: user.name }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Log out", onClick: () => {
73
73
  setIsLoggingOut(true);
74
- logout().catch(() => setIsLoggingOut(false));
74
+ // A failed request still ends the session on screen
75
+ // (AuthProvider), so this stays a deliberate logout: a
76
+ // clean /login, never the previous user's page as `from`.
77
+ logout().catch(() => { });
75
78
  }, children: _jsx(LogOutIcon, {}) })] }) })] }) }), _jsxs(SidebarInset, { className: "min-w-0", children: [_jsxs("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b border-border px-4", children: [_jsx(SidebarTrigger, {}), _jsx("div", { className: "flex-1" }), _jsx(ThemeToggle, {})] }), _jsx("div", { className: "flex-1 p-6", children: _jsx(Outlet, {}) })] })] }), _jsx(Toaster, { position: "top-center" })] }));
76
79
  }
@@ -1,7 +1,7 @@
1
1
  // The address a link in the rich-text editor points at, from what the
2
2
  // person typed. Web and email addresses only: a bare `example.com` becomes
3
3
  // `https://example.com` and a bare `name@example.com` a `mailto:`; a path
4
- // on this site (`/x`) or a heading (`#x`) is kept. Any other scheme
4
+ // on this site (`/x`, never `//x`) or a heading (`#x`) is kept. Any other scheme
5
5
  // (`javascript:`, `data:`, `file:`) is refused, so a link can't run code
6
6
  // or reach a local file.
7
7
  /** The `href` to save, `''` to remove the link, or `null` when it's refused. */
@@ -14,6 +14,10 @@ export function linkHref(typed) {
14
14
  // A scheme of its own (`javascript:`), not a port (`example.com:8080`).
15
15
  if (/^[a-z][a-z0-9+.-]*:(?!\d)/i.test(value))
16
16
  return null;
17
+ // `//evil.com` (and `/\evil.com`, which browsers read the same way) is
18
+ // another site, not a path on this one.
19
+ if (/^\/[/\\]/.test(value))
20
+ return null;
17
21
  if (value.startsWith('/') || value.startsWith('#'))
18
22
  return value;
19
23
  if (/^[^\s@/]+@[^\s@/]+\.[^\s@/]+$/.test(value))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tristan2828/ui-foundation",
3
- "version": "3.27.0",
3
+ "version": "3.27.2",
4
4
  "description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
5
5
  "license": "MIT",
6
6
  "repository": {