{
const settings = useSettings()
return (
- {settings.principal}
{settings.settings.agentEndpoint}
@@ -100,49 +99,6 @@ describe('SettingsContext', () => {
expect(screen.getByTestId('require-auth')).toHaveTextContent('false')
})
- it('handles unauthenticated user', async () => {
- const { getSessionToken } = await import('../../token')
- vi.mocked(getSessionToken).mockReturnValue(undefined)
-
- render(
-
-
-
- )
-
- expect(screen.getByTestId('principal')).toHaveTextContent(
- 'unauthenticated'
- )
- })
-
- it('handles token decoding errors', async () => {
- const { getSessionToken } = await import('../../token')
- vi.mocked(getSessionToken).mockReturnValue('invalid-token')
-
- const { jwtDecode } = await import('jwt-decode')
- vi.mocked(jwtDecode).mockImplementation(() => {
- throw new Error('Invalid token')
- })
-
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
-
- render(
-
-
-
- )
-
- expect(screen.getByTestId('principal')).toHaveTextContent(
- 'unauthenticated'
- )
- expect(consoleSpy).toHaveBeenCalledWith(
- 'Error decoding token',
- expect.any(Error)
- )
-
- consoleSpy.mockRestore()
- })
-
it('uses provided requireAuth prop', async () => {
const { getSessionToken } = await import('../../token')
vi.mocked(getSessionToken).mockReturnValue('mock-token')
diff --git a/packages/react-components/src/index.tsx b/packages/react-components/src/index.tsx
index bfb3129..41ebae7 100644
--- a/packages/react-components/src/index.tsx
+++ b/packages/react-components/src/index.tsx
@@ -4,7 +4,12 @@ export * from './contexts'
export * from './runme/client'
export { TypingCell, ChatSequence } from './components/Chat/Chat'
export * from './components/Actions/icons'
-export { generateSessionName } from './storage'
+export {
+ generateSessionName,
+ type ISessionStorage,
+ type SessionNotebook,
+ type SessionRecord,
+} from './storage'
// Export layout
export { default as Layout } from './layout'
diff --git a/packages/react-components/src/layout.tsx b/packages/react-components/src/layout.tsx
index 8bbcc34..af5ea92 100644
--- a/packages/react-components/src/layout.tsx
+++ b/packages/react-components/src/layout.tsx
@@ -7,6 +7,7 @@ import { Box, Flex, Text } from '@radix-ui/themes'
import { AppBranding } from './App'
import TopNavigation from './components/TopNavigation'
import { useSettings } from './contexts/SettingsContext'
+import { getSessionToken } from './token'
function Layout({
branding,
@@ -67,7 +68,9 @@ function Layout({
-
+ }
+ />
diff --git a/packages/react-components/src/storage.ts b/packages/react-components/src/storage.ts
index 2e47b41..bf07878 100644
--- a/packages/react-components/src/storage.ts
+++ b/packages/react-components/src/storage.ts
@@ -24,7 +24,37 @@ export interface SessionRecord {
export type SessionNotebook = SessionRecord
-export class SessionStorage extends Dexie {
+/**
+ * Storage interface for session persistence.
+ * Implementations can use IndexedDB (Dexie), Supabase Postgres, or other backends.
+ */
+export interface ISessionStorage {
+ /** The owner/user identifier, used for scoping queries and RLS */
+ readonly principal: string
+
+ /** Save or update a notebook for a session */
+ saveNotebook(id: string, notebook: Notebook): Promise | void
+
+ /** Load a single session by id */
+ loadSession(id: string): Promise
+
+ /** Load multiple sessions by their ids */
+ loadSessions(ids: string[]): Promise
+
+ /** List all sessions for the principal (sorted by updated desc) */
+ listSessions(): Promise
+
+ /** List session ids that are still active (created in last 24 hours and valid) */
+ listActiveSessions(): Promise
+
+ /** Delete a session by id */
+ deleteSession(id: string): Promise
+
+ /** Create a new session and return its id */
+ createSession(): Promise
+}
+
+export class DexieSessionStorage extends Dexie implements ISessionStorage {
sessions!: Table
readonly principal: string
readonly client: RunnerClient
@@ -159,3 +189,6 @@ export class SessionStorage extends Dexie {
export function generateSessionName(): string {
return new Date().toISOString()
}
+
+/** @deprecated Use DexieSessionStorage directly. Alias for backwards compatibility. */
+export { DexieSessionStorage as SessionStorage }