Skip to content

Conversation

@Anchel123
Copy link
Contributor

@Anchel123 Anchel123 commented Dec 1, 2025

Summary by CodeRabbit

  • New Features

    • Added HTTP endpoints to manage graph elements and schema element attributes.
  • Improvements

    • Selector & toolbar: refined add-node/add-edge controls, layout, and performance optimizations.
    • Controls: tighter single-row layout and updated button styling.
    • Settings: reduced page padding, JSON-based config requests, table item height, and added config name field.
    • Token handling: improved API cleanup and typed responses.
  • Tests / Chores

    • End-to-end token test file commented out.
    • CI workflow job disabled.

✏️ Tip: You can customize this high-level summary in your review settings.

…readability. Added GET method for schema elements, updated controls layout, and removed unused effects in Selector. Adjusted Configurations component for better styling and commented out Tokens section in Settings.
@Anchel123 Anchel123 requested a review from barakb December 1, 2025 08:42
@vercel
Copy link

vercel bot commented Dec 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
falkordb-browser Ready Ready Preview Comment Dec 1, 2025 10:04am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

Walkthrough

This PR disables the run-token-tests workflow job, adds route handlers for graph elements and schema attributes with session-based access control, refactors graph UI (Selector and controls) to use node/edge add-state and layout tweaks, updates Settings UI and API payloads, and reorganizes e2e API response types and token test utilities with temporary-token cleanup.

Changes

Cohort / File(s) Summary
Workflow Configuration
​.github/workflows/playwright.yml
Commented out the run-token-tests job (disabled token-based test execution).
Graph API Endpoints
app/api/graph/[graph]/[element]/route.ts
New GET/POST/DELETE route handler for graph elements; validates bodies, authenticates via getClient, enforces read-only vs full access, and uses roQuery/query.
Schema API Endpoints
app/api/schema/[schema]/[element]/[key]/_route.ts
New GET/PATCH/DELETE route handler for schema element attributes; validates input, authenticates, conditionally updates node or relationship attributes, and respects access level.
Graph UI Component Updates
app/graph/Selector.tsx
Replaced add-entity/add-relation with add-node/add-edge across public props; added isAddNode/isAddEdge flags and setters; introduced useMemo for filters, improved history handling, and updated Toolbar props (including graphName).
Graph Controls Layout
app/graph/controls.tsx
Simplified layout to a single flex row, tightened spacing, and updated button styling (pointer-events and padding) without behavioral changes.
Settings Configuration UI
app/settings/Configurations.tsx
Switched config POST to JSON body with Content-Type; added top-level name to config rows; wrapped table and set itemHeight={60}.
Settings Page Layout
app/settings/page.tsx
Reduced container padding (p-12p-2); refactored admin button rendering (Browser Settings always shown; Configurations & Users shown for admin when online); tokens button remains commented.
E2E Token Types
e2e/logic/api/responses/tokenResponse.ts
New interfaces: Token, ListTokensResponse, TokenDetailsResponse.
E2E API Calls
e2e/logic/api/apiCalls.ts
Updated listTokens() and getTokenDetails() types to use new interfaces; added temporary-token cleanup in listTokensAsUser() and revokeTokenAsUser(); revokeAllUserTokens() uses Promise.allSettled with aggregated errors.
E2E Tests (Placeholders)
e2e/tests/settingsTokens.spec.ts
New Playwright test file added but fully commented out (no executable tests).
E2E Response Types Reorganization
e2e/logic/api/responses/*
Consolidated many removed individual response interfaces and added grouped files: moved/added graphResponses.ts, schemaResponses.ts, settingsResponses.ts, userResponses.ts, authResponses.ts; removed many old single-purpose response files.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Route as Route Handler
    participant Auth as getClient
    participant DB as Query Engine
    participant Logger as Console

    Client->>Route: HTTP {GET/POST/PATCH/DELETE} /api/...
    Route->>Auth: getClient(request)
    Auth-->>Route: client (session, role)
    Route->>Route: validateBody(request) (if applicable)
    alt validation fails
        Route->>Logger: log validation error
        Route-->>Client: 400 + { error }
    else validation succeeds
        alt role == full
            Route->>DB: query(...)
        else
            Route->>DB: roQuery(...)
        end
        DB-->>Route: result / affected rows
        Route-->>Client: 200 + JSON result
    end
    alt server error
        Route->>Logger: log error
        Route-->>Client: 500 + { error }
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on:
    • API route handlers (app/api/graph/..., app/api/schema/...) — ensure consistent access control, validation, and query construction for node vs relationship paths.
    • Selector prop/API changes (app/graph/Selector.tsx) — ensure all consumers updated and typings consistent.
    • E2E API changes (e2e/logic/api/apiCalls.ts) — verify temporary-token cleanup and aggregated error handling behave as intended.
    • Response type reorganization (e2e/logic/api/responses/*) — check imports across tests/utilities to avoid broken references.

Poem

✨ Tokens paused, routes arise,
Nodes and edges heed new ties,
Selectors shift, controls align,
Configs trimmed to fit the line,
Tests wait quiet — change takes flight.

Suggested PR titles:

  • feat(api): add graph and schema attribute route handlers with session-based access
  • refactor(ui): switch add-entity/add-relation to add-node/add-edge and simplify controls layout
  • chore(e2e): reorganize API response types and add token cleanup in test utilities
  • chore(ci): disable run-token-tests workflow job

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Remove personal access token' is vague and does not clearly describe the scope of changes. The PR contains extensive refactoring across multiple files, API route additions, UI component updates, response type reorganization, and workflow modifications—far beyond just removing a token feature. Consider a more specific title using conventional commit format, such as 'refactor: reorganize API response types and disable token tests' or break changes into focused PRs addressing specific concerns (token feature removal, response type consolidation, UI updates).
Docstring Coverage ⚠️ Warning Docstring coverage is 44.87% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch remove-personal-access-token

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (9)
e2e/infra/utils.ts (1)

21-29: Change refreshInterval e2e default from "60" to "10" to match app initialization

The e2e default of "60" is the maximum slider value and does not match the app's actual defaults. The app initializes refreshInterval to 10 seconds (app/providers.tsx:70) with a localStorage fallback of 30 seconds (app/providers.tsx:420). Using the maximum value in e2e tests could mask bugs and create inconsistency. Change the e2e default to "10" to match the app's primary initialization.

app/components/graph/UploadGraph.tsx (1)

58-58: Form submission doesn't prevent default.

The onSubmit handler should prevent the default form submission behavior to avoid page reload.

-                <form onSubmit={onUploadData} className="grow p-4 flex flex-col gap-6">
+                <form onSubmit={(e) => { e.preventDefault(); onUploadData(); }} className="grow p-4 flex flex-col gap-6">
app/settings/users/Users.tsx (1)

159-161: Non-null assertion on newUser could throw.

The handleSetRole(newUser!) assumes newUser is never null when clicked, but there's no guard. Consider disabling the button when newUser is null.

-                        <Button onClick={() => handleSetRole(newUser!)}>Set User</Button>
+                        <Button 
+                            onClick={() => newUser && handleSetRole(newUser)}
+                            disabled={!newUser}
+                        >
+                            Set User
+                        </Button>
app/components/TableComponent.tsx (3)

183-199: Duplicate useEffect for focusing search input.

Lines 183-187 and 195-199 both focus searchRef.current on mount. Remove one of these duplicate effects.

- useEffect(() => {
-     if (searchRef.current) {
-         searchRef.current.focus()
-     }
- }, [])
-
  useEffect(() => {
      if (inputRef && inputRef.current && editable) {
          inputRef.current.focus()
      }
  }, [inputRef, editable])

  useEffect(() => {
      if (searchRef.current) {
          searchRef.current.focus()
      }
  }, [])

493-499: Avoid mutating row objects directly.

The checkbox change handler mutates r.checked directly instead of returning a new object. This can cause React reconciliation issues since the object reference doesn't change.

  onCheckedChange={() => {
-     setRows(rows.map((r) => {
-         if (r.name === row.name) {
-             r.checked = !r.checked
-         }
-         return r
-     }))
+     setRows(prevRows => prevRows.map((r) => 
+         r.name === row.name 
+             ? { ...r, checked: !r.checked } 
+             : r
+     ))
  }}

377-392: Same mutation issue in header checkbox.

The "select all" checkbox handler also mutates row objects directly.

  onCheckedChange={() => {
      const checked = rows.every(row => row.checked)
-     setRows(rows.map((row) => {
-         row.checked = !checked
-         return row
-     }))
+     setRows(rows.map((row) => ({
+         ...row,
+         checked: !checked
+     })))
  }}
app/api/schema/[schema]/[element]/label/_route.ts (1)

2-56: Sanitize label and validate elementId to avoid Cypher injection and invalid IDs

The switch to validateBody and parameterized $id is a solid step forward, but two things are still worth tightening up here:

  1. Label safety:
    label comes from the request body and is interpolated directly into the Cypher in both handlers:

    const query = `MATCH (n) WHERE ID(n) = $id SET n:${label}`;
    // and
    const query = `MATCH (n) WHERE ID(n) = $id REMOVE n:${label}`;

    Since the Zod schemas for addSchemaElementLabel/removeSchemaElementLabel only enforce “non‑empty string”, a crafted label could break the query or be used for injection. At minimum, enforce an allowed character set (e.g., labels starting with a letter/underscore and containing only letters, digits, and underscores) before building the query.

    For example:

  •  const { label } = validation.data;
    
  •  const { label } = validation.data;
    
  •  // Ensure label is in a safe format (alphanumeric + underscore) to avoid Cypher injection
    
  •  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(label)) {
    
  •    return NextResponse.json(
    
  •      { message: "Invalid label format" },
    
  •      { status: 400 }
    
  •    );
    
  •  }
    
    
    Apply the same check in both POST and DELETE.
    
    
  1. Element ID validation:
    const elementId = Number(node); will yield NaN for bad input, which then goes into { params: { id: elementId } }. You could fail fast instead:

  • const elementId = Number(node);
  • const elementId = Number(node);
  • if (!Number.isInteger(elementId) || elementId < 0) {
  •  return NextResponse.json(
    
  •    { message: "Invalid element id" },
    
  •    { status: 400 }
    
  •  );
    
  • }

These changes would close the remaining gaps while keeping the current route structure intact.




Also applies to: 59-112

</blockquote></details>
<details>
<summary>app/api/schema/[schema]/[element]/_route.ts (1)</summary><blockquote>

`36-41`: **Incorrect empty array check for `label`.**

`label` is `z.array(z.string())`, so `!label` will always be `false` (empty arrays are truthy). This validation never triggers.



```diff
    if (!type) {
-        if (!label) throw new Error("Label is required");
+        if (!label || label.length === 0) throw new Error("Label is required");

      if (!selectedNodes || selectedNodes.length !== 2)
        throw new Error("Selected nodes are required");
    }
app/api/auth/[...nextauth]/options.ts (1)

10-20: isValidJWTPayload should validate jti presence since it's required in CustomJWTPayload and directly used at line 279.

The type guard at line 189 only validates sub, host, and port but doesn't check for jti, despite it being marked as required in the interface. When getPasswordFromTokenDB(payload.jti) is called at line 279, a missing jti would pass undefined to a function expecting a string. Add jti validation to the type guard: return Boolean(p.sub && p.host && p.port && p.jti);

🟡 Minor comments (20)
app/components/TableComponent.tsx-92-169 (1)

92-169: Stale closure risk in lazy loading check.

The handleLoadLazyCell callback captures loadingCells and loadedCells in its closure, but these checks happen synchronously at call time. Due to React's batched state updates, multiple cells could pass the guard simultaneously before state updates propagate.

Consider using refs for the tracking sets or moving the check inside the state setter:

- // If already loading or loaded, don't load again
- if (loadingCells.has(cellKey) || loadedCells.has(cellKey)) {
-     return;
- }
+ // Use functional update to check current state atomically
+ setLoadingCells(prev => {
+     if (prev.has(cellKey) || loadedCells.has(cellKey)) {
+         return prev; // No change, already loading/loaded
+     }
+     // Mark as loading and proceed
+     setTimeout(() => loadFn().then(...), 0);
+     return new Set(prev).add(cellKey);
+ });

Alternatively, since loadAttemptedRef already tracks attempts synchronously, the current implementation may work in practice.

Committable suggestion skipped: line range outside the PR's diff.

app/api/chat/route.tsx-66-76 (1)

66-76: Non-standard SSE format for validation errors.

The error event on line 67 uses event: error status: ${400} data: ... which doesn't follow the SSE specification. Standard format uses separate lines for event type and data.

Consider aligning with the format used elsewhere in this file (e.g., lines 137, 142):

-writer.write(encoder.encode(`event: error status: ${400} data: ${JSON.stringify(validation.error)}\n\n`))
+writer.write(encoder.encode(`event: error\ndata: ${JSON.stringify({ message: validation.error, status: 400 })}\n\n`))
app/api/chat/route.tsx-5-5 (1)

5-5: Trailing slash inconsistency in URL construction.

The default value includes a trailing slash, but if CHAT_URL is set without one, the URL concatenation on lines 16 and 91 will produce malformed URLs (e.g., http://example.comconfigured-model).

Consider normalizing the URL:

-const CHAT_URL = process.env.CHAT_URL || "http://localhost:8000/"
+const CHAT_URL = (process.env.CHAT_URL || "http://localhost:8000").replace(/\/$/, "");

Then update the fetch calls:

-const response = await fetch(`${CHAT_URL}configured-model`, {
+const response = await fetch(`${CHAT_URL}/configured-model`, {
-const response = await fetch(`${CHAT_URL}text_to_cypher`, {
+const response = await fetch(`${CHAT_URL}/text_to_cypher`, {

Committable suggestion skipped: line range outside the PR's diff.

app/api/graph/[graph]/[element]/[key]/route.ts-24-24 (1)

24-24: Validate element param parses to a valid number.

Number(element) will produce NaN for invalid strings, which may cause unexpected behavior in the query.

 const elementId = Number(element);
+
+if (Number.isNaN(elementId)) {
+  return NextResponse.json(
+    { message: "Invalid element ID" },
+    { status: 400 }
+  );
+}
app/schema/DataPanel.tsx-516-516 (1)

516-516: Same optional chaining inconsistency.

Apply the same fix here for consistency with line 353.

-                                            session?.user.role !== "Read-Only" && (
+                                            session?.user?.role !== "Read-Only" && (
app/schema/DataPanel.tsx-337-337 (1)

337-337: Inconsistent optional chaining on user.role

Line 337 uses session?.user.role but line 353 uses session?.user?.role. If session?.user could be undefined, direct property access to .role would throw. Align the optional chaining pattern across all occurrences (lines 337, 516, 715 vs 353).

-                                type && session?.user.role !== "Read-Only" &&
+                                type && session?.user?.role !== "Read-Only" &&
app/schema/DataPanel.tsx-715-715 (1)

715-715: Same optional chaining inconsistency.

Apply the same fix here for consistency with line 353.

-                        session?.user.role !== "Read-Only" &&
+                        session?.user?.role !== "Read-Only" &&
app/api/graph/[graph]/count/edges/route.ts-65-68 (1)

65-68: Potential double-close on abort handler.

The abort listener is registered after the stream may already be closed (lines 39, 50, 62). Calling writer.close() on an already-closed writer throws. Wrap in try-catch or guard with a flag.

+  let closed = false;
+  const safeClose = () => {
+    if (!closed) {
+      closed = true;
+      writer.close();
+    }
+  };

   // Clean up if the client disconnects early
   request.signal.addEventListener("abort", () => {
-    writer.close();
+    safeClose();
   });

Then replace other writer.close() calls with safeClose().

Committable suggestion skipped: line range outside the PR's diff.

app/api/graph/[graph]/[element]/route.ts-152-152 (1)

152-152: Missing NaN validation for elementId in DELETE.

Same issue as GET - validate the parsed element ID.

app/api/graph/[graph]/[element]/route.ts-22-22 (1)

22-22: Missing NaN validation for elementId.

Number(element) will produce NaN for non-numeric strings, which could cause unexpected query behavior. Validate before use.

     const { graph: graphId, element } = await params;
     const elementId = Number(element);
+
+    if (Number.isNaN(elementId)) {
+      return NextResponse.json(
+        { message: "Invalid element ID" },
+        { status: 400 }
+      );
+    }
app/api/auth/tokens/[tokenId]/route.ts-226-232 (1)

226-232: Revoker user lookup may fail silently.

The query assumes the revoker user exists. If authenticatedUser.username doesn't match any User node (e.g., JWT-only auth without a stored user), the MATCH (revoker:User ...) will yield no results, and the revocation fails with a generic error.

Consider using OPTIONAL MATCH for the revoker or creating the relationship conditionally:

     const revokeQuery = `
       MATCH (t:Token {token_id: '${escapeString(tokenId)}'})-[:BELONGS_TO]->(u:User)
-      MATCH (revoker:User {username: '${escapeString(revokerUsername)}'})
+      OPTIONAL MATCH (revoker:User {username: '${escapeString(revokerUsername)}'})
       SET t.is_active = false
-      CREATE (t)-[:REVOKED_BY {at: ${nowUnix}}]->(revoker)
+      FOREACH (_ IN CASE WHEN revoker IS NOT NULL THEN [1] ELSE [] END |
+        CREATE (t)-[:REVOKED_BY {at: ${nowUnix}}]->(revoker)
+      )
       RETURN t.token_id as token_id
     `;
app/graph/CreateElementPanel.tsx-144-149 (1)

144-149: Global Escape listener may conflict with nested dialogs.

The component registers a window-level keydown listener that closes the panel on Escape. This could interfere with nested DialogComponent instances (e.g., delete confirmation) where Escape should only close the inner dialog.

Consider stopping propagation in nested dialogs or checking if the panel itself should handle the escape:

     useEffect(() => {
+        const handler = (e: KeyboardEvent) => {
+            // Only close if no dialog is open
+            if (e.key === "Escape" && !document.querySelector('[role="dialog"][data-state="open"]')) {
+                handleClose(e)
+            }
+        }
-        window.addEventListener("keydown", handleClose)
+        window.addEventListener("keydown", handler)
         return () => {
-            window.removeEventListener("keydown", handleClose)
+            window.removeEventListener("keydown", handler)
         }
     }, [handleClose])
app/graph/DataPanel.tsx-162-162 (1)

162-162: Optional chaining removed on user.role access.

Changed from session?.user?.role to session?.user.role. If session.user is undefined, this will throw a runtime error. The original optional chaining was safer.

-                                type && l && session?.user.role !== "Read-Only" &&
+                                type && l && session?.user?.role !== "Read-Only" &&
app/graph/DataPanel.tsx-181-181 (1)

181-181: Same optional chaining issue.

Apply consistent optional chaining for role access.

-                            type && (labelsHover || label.length === 0) && session?.user?.role !== "Read-Only" &&
+                            type && (labelsHover || label.length === 0) && session?.user?.role !== "Read-Only" &&

Committable suggestion skipped: line range outside the PR's diff.

app/api/auth/tokens/credentials/route.ts-205-209 (1)

205-209: Silent storage failure allows unmanageable tokens.

When token storage fails, the token is still returned to the user. This creates "orphan" tokens that work but can't be revoked via the UI. Consider returning the storage failure to the client or at least including a warning in the response.

    } catch (storageError) {
      // eslint-disable-next-line no-console
      console.error('Failed to store token in FalkorDB for user:', authenticatedUser.username, storageError);
-     // Continue - token will still work but can't be managed via UI
+     return NextResponse.json(
+       { message: "Token created but storage failed - token cannot be managed via UI", token },
+       { status: 201 }
+     );
    }

Committable suggestion skipped: line range outside the PR's diff.

e2e/logic/POM/settingsTokensPage.ts-295-302 (1)

295-302: Unused return value in verifyTokenExists.

isTokenRowVisible returns a boolean, but it's not used. If interactWhenVisible throws when the element isn't visible, this logic works. However, if it returns false, the function incorrectly returns true. Consider clarifying the intent.

  async verifyTokenExists(name: string): Promise<boolean> {
    try {
-     await this.isTokenRowVisible(name);
-     return true;
+     return await this.isTokenRowVisible(name);
    } catch {
      return false;
    }
  }
app/providers.tsx-175-175 (1)

175-175: Duplicate dependency in useMemo array.

navigateToSettings appears twice in the dependency array.

-  }), [displayChat, navigateToSettings, contentPersistence, defaultQuery, hasChanges, lastLimit, limit, model, navigateToSettings, newContentPersistence, newDefaultQuery, newLimit, newModel, newRefreshInterval, newRunDefaultQuery, newSecretKey, newTimeout, refreshInterval, runDefaultQuery, secretKey, timeout, displayTextPriority, newDisplayTextPriority, replayTutorial, tutorialOpen])
+  }), [displayChat, navigateToSettings, contentPersistence, defaultQuery, hasChanges, lastLimit, limit, model, newContentPersistence, newDefaultQuery, newLimit, newModel, newRefreshInterval, newRunDefaultQuery, newSecretKey, newTimeout, refreshInterval, runDefaultQuery, secretKey, timeout, displayTextPriority, newDisplayTextPriority, replayTutorial, tutorialOpen, showMemoryUsage])

Note: Also added showMemoryUsage since it's used in settings.graphInfo.

app/api/swagger/swagger-spec.ts-94-98 (1)

94-98: Port should be type integer per OpenAPI best practices.

The port field is defined as type string in both the Swagger spec and Zod validation schema, but the implementation in app/api/auth/[...nextauth]/options.ts (line 63) explicitly parses it to an integer: parseInt(credentials.port, 10). According to OpenAPI specifications, port numbers should be represented as type: "integer" with format: "int32" and validation constraints (minimum: 1, maximum: 65535). Update the Swagger schema to reflect the semantic type and consider using Zod's .coerce.number() for validation consistency.

app/graph/page.tsx-192-202 (1)

192-202: Missing leading slash in API URL

Line 194 uses api/graph/... but other API calls in this file (e.g., line 104) use /api/graph/.... This inconsistency could cause routing issues depending on the current browser path.

-        const result = await securedFetch(`api/graph/${prepareArg(graphName)}/${fakeId}`, {
+        const result = await securedFetch(`/api/graph/${prepareArg(graphName)}/${fakeId}`, {
app/schema/CreateElementPanel.tsx-635-638 (1)

635-638: Type assertion may be unsafe

The filter prev.filter((e): e is Node => !!e.labels) doesn't guarantee exactly two nodes. If the filtered result has more or fewer elements, the as [Node, Node] assertion could lead to runtime issues.

Consider adding a guard or using the existing isTwoNodes utility:

-                onClick={() => setSelectedNodes && setSelectedNodes(prev => {
-                  const nodes = prev.filter((e): e is Node => !!e.labels) as [Node, Node]
-                  return [nodes[1], nodes[0]]
-                })}
+                onClick={() => setSelectedNodes && setSelectedNodes(prev => {
+                  const nodes = prev.filter((e): e is Node => !!e.labels)
+                  if (nodes.length !== 2) return prev
+                  return [nodes[1], nodes[0]]
+                })}
🧹 Nitpick comments (56)
app/globals.css (1)

52-57: Verify visual impact of lighter font weight.

The SofiaSans font weight reduced from 400 to 200 is significantly lighter. Combined with the added word-spacing, this could affect readability—especially for body text or longer content. Please verify the visual appearance in the UI matches design intent.

app/api/monitor/route.ts (1)

48-48: Good addition for debugging visibility.

Adding console.error(err) before the 500 response improves observability and aligns with the inner catch pattern at line 41.

Optional enhancement: Consider structured logging (e.g., using a logging library) with error sanitization to prevent potential exposure of sensitive data in production logs.

app/graph/TableView.tsx (1)

17-22: Consider using Set for more efficient headers computation.

The current reduce + includes() approach has O(n×m×h) complexity. Using a Set would reduce it to O(n×m):

-        const headers = graph.Data.reduce<string[]>((acc, row) => {
-            Object.keys(row).forEach((key) => {
-                if (!acc.includes(key)) acc.push(key);
-            });
-            return acc;
-        }, []);
+        const headersSet = new Set<string>();
+        graph.Data.forEach((row) => {
+            Object.keys(row).forEach((key) => headersSet.add(key));
+        });
+        const headers = Array.from(headersSet);
app/graph/controls.tsx (1)

52-59: Consider adding an aria-label to the Switch for screen reader users.

The Switch is wrapped in a Tooltip which provides visual context ("Animation Control") on hover, but screen readers may not automatically associate the tooltip content with the Switch control.

Apply this diff to improve accessibility:

                            <Switch
                                data-testid="animationControl"
+                               aria-label="Animation Control"
                                className="pointer-events-auto data-[state=unchecked]:bg-border"
                                checked={cooldownTicks === undefined}
                                onCheckedChange={() => {
                                    handleCooldown(cooldownTicks === undefined ? 0 : undefined, false)
                                }}
                            />
e2e/infra/ui/browserWrapper.ts (1)

71-85: Teardown order looks good; consider clearing browser/context references after close

Calling await this.closePage() before closing the browser is a solid improvement and matches the comment about clearing intervals first. If a BrowserWrapper instance is ever reused after closeBrowser runs, though, createNewPage will see non‑null this.browser/this.context and try to use already‑closed Playwright objects.

To make reuse safe and avoid subtle “browser/context already closed” errors, consider explicitly nulling these references after a successful close:

    async closeBrowser() {
        // Close the page first (which clears intervals) before closing the browser
        await this.closePage();
-        
-        if (this.browser) {
-            await this.browser.close();
-        }
+
+        if (this.browser) {
+            await this.browser.close();
+            this.browser = null;
+        }
+
+        // Context becomes invalid once the browser is closed
+        this.context = null;
     }

Possible conventional‑commit style PR titles that fit this change set:

  • test(e2e): close page before browser in BrowserWrapper
  • chore(e2e): harden Playwright browser teardown
.github/workflows/release-image.yml (1)

20-28: Add error handling and timeout for GitHub API call.

The curl command fetching the latest release has no timeout, error handling, or response validation. If the API is unavailable or rate-limited, CYPHER_VERSION will be empty, causing a downstream build failure that's hard to debug.

Consider:

  • Adding --max-time flag for timeout
  • Checking curl exit code
  • Validating that LATEST_VERSION is not empty before using it
  • Adding a fallback version as safety net
  - name: Set up variables
    run: |
      if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
        echo "CYPHER_VERSION=${{ github.event.inputs.cypher_version }}" >> $GITHUB_ENV
      else
        # Get the latest released version from GitHub releases
-       LATEST_VERSION=$(curl -s https://api.github.com/repos/falkordb/text-to-cypher/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+       LATEST_VERSION=$(curl -s --max-time 10 https://api.github.com/repos/falkordb/text-to-cypher/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' || echo "")
+       if [ -z "$LATEST_VERSION" ]; then
+         echo "Failed to fetch latest version, using fallback: v0.1.5"
+         LATEST_VERSION="v0.1.5"
+       fi
        echo "CYPHER_VERSION=$LATEST_VERSION" >> $GITHUB_ENV
      fi
Dockerfile (1)

1-5: Dockerfile CYPHER_VERSION default differs from workflow.

The Dockerfile defaults to latest while the workflow defaults to v0.1.5. For build reproducibility, consider aligning these defaults.

-ARG CYPHER_VERSION=latest
+ARG CYPHER_VERSION=v0.1.5
app/components/EditorComponent.tsx (1)

60-62: Centralized sizing constants are good; double-check lineHeight vs fontSize, especially in dialog

Centralizing MAX_HEIGHT, LINE_HEIGHT, and PLACEHOLDER, and wiring LINE_HEIGHT through monacoOptions, editorHeight, and the toolbar container improves consistency.

One thing to verify: in the dialog editor you now use lineHeight: LINE_HEIGHT (22) with fontSize: 25. Depending on Monaco’s font metrics, this can make lines feel cramped or even clip ascenders/descenders. In the main editor fontSize (20) is closer to LINE_HEIGHT (22), so it’s less of a concern there.

Consider either:

  • Using a slightly larger line height for the dialog (e.g. a separate DIALOG_LINE_HEIGHT), or
  • Deriving LINE_HEIGHT from the chosen fontSize (e.g. Math.round(fontSize * 1.3)), at least for the larger dialog editor.

Also applies to: 79-82, 677-677, 765-767

e2e/tests/cluster.spec.ts (2)

75-86: Prefer a simple for...of loop over reduce for sequential async steps

Functionally this works, but the Promise reduce pattern makes the test harder to read and reason about than a straightforward loop in an async test body. A plain for...of with await keeps the same sequential semantics and matches common Playwright style.

You could simplify as:

-      await CLUSTER_NODES.reduce(
-        (chain, node) =>
-          chain.then(async () => {
-            await loginPage.fillHost(node.host);
-            await loginPage.fillPort(node.port);
-            expect(await loginPage.getHost()).toBe(node.host);
-            expect(await loginPage.getPort()).toBe(node.port);
-            await loginPage.fillHost("");
-            await loginPage.fillPort("");
-          }),
-        Promise.resolve()
-      );
+      for (const node of CLUSTER_NODES) {
+        await loginPage.fillHost(node.host);
+        await loginPage.fillPort(node.port);
+        expect(await loginPage.getHost()).toBe(node.host);
+        expect(await loginPage.getPort()).toBe(node.port);
+        await loginPage.fillHost("");
+        await loginPage.fillPort("");
+      }

If you want slightly stronger coverage, you could also assert that the fields are empty after the reset step.


115-117: Tidy up or replace commented-out schema navigation assertions

The comment explains why schema navigation is disabled, but leaving assertions commented-out tends to accumulate dead code and can confuse future readers about the intended behavior.

Two options to consider:

  • If schema navigation is permanently removed: delete these lines entirely so the test reflects the current feature set.
  • If the behavior changed (e.g., no schema tab anymore): replace with an explicit assertion for the new behavior (e.g., schema button not visible, or alternative navigation path).

Either way, having active expectations rather than commented code will keep this test more maintainable.

app/components/PaginationList.tsx (1)

304-336: Next 5 pages button reorder is fine; consider aligning multi-page navigation behavior

The layout change to place ">>" after ">" looks good and is more conventional. Functionally, the logic for the "Next 5 pages" button remains consistent.

Two small, optional cleanups you might consider while touching this area:

  1. Consistent focus behavior for multi-page jumps
  • Line 305 ("Previous 5 pages") uses setStepCounter directly, while line 335 ("Next 5 pages") uses handleSetStepCounter, which also refocuses the search input.
  • For consistent keyboard/focus behavior when jumping by 5 pages, consider routing the previous-5 button through handleSetStepCounter as well:
-                    <Button disabled={stepCounter < 4} label="<<" title="Previous 5 pages" onClick={() => setStepCounter(prev => prev > 4 ? prev - 5 : prev)} />
+                    <Button
+                        disabled={stepCounter < 4}
+                        label="<<"
+                        title="Previous 5 pages"
+                        onClick={() => handleSetStepCounter(prev => prev > 4 ? prev - 5 : prev)}
+                    />
  1. Avoid enabled-but-no-op state on "Previous 5 pages"
  • With disabled={stepCounter < 4} and the guard prev > 4 ? prev - 5 : prev, the button is enabled at stepCounter === 4 but the click is a no-op.
  • If you want the button disabled whenever a 5-page jump isn’t possible, adjust the disabled condition:
-                    <Button
-                        disabled={stepCounter < 4}
+                    <Button
+                        disabled={stepCounter <= 4}
                         label="<<"
                         title="Previous 5 pages"
                         onClick={() => handleSetStepCounter(prev => prev > 4 ? prev - 5 : prev)}
                     />

These are minor UX consistency tweaks and not blockers.

Suggested conventional-commit style PR title: fix: improve pagination navigation order.

components/ui/drawer.tsx (1)

58-60: Consider simplifying handle markup and marking it as presentational

The handle’s className expression is quite dense, and since it’s a purely visual affordance (the drawer itself handles interaction), you can improve readability and clarify accessibility intent with a small refactor.

For example:

-      <div className={cn("mx-auto mt-2 h-2 w-[100px] rounded-full bg-muted cursor-grab active:cursor-grabbing", (side === "left" || side === "right") && "w-2 h-[100px] my-auto", side === "left" && "mr-2", side === "right" && "ml-2")} />
+      <div
+        aria-hidden="true"
+        className={cn(
+          "mx-auto mt-2 h-2 w-[100px] rounded-full bg-muted cursor-grab active:cursor-grabbing",
+          (side === "left" || side === "right") && "w-2 h-[100px] my-auto",
+          side === "left" && "mr-2",
+          side === "right" && "ml-2",
+        )}
+      />

This keeps behavior the same, but makes the intent clearer for future readers and assistive tech.

app/login/LoginForm.tsx (1)

152-158: URL validation regex and message look sound; consider extraction + tests

The new regex correctly enforces falkor[s]:// and redis[s]:// with optional username:password@, host, port, and /db-number, and the error message matches that contract. For readability and future updates, consider extracting the pattern (and perhaps a small validator helper) to a shared constant and adding unit tests for typical positive/negative cases.

app/graph/DeleteElement.tsx (1)

31-44: Redundant setIsLoading(false) in finally block.

When setOpen(false) is called on Line 41, the useEffect (Lines 31-35) will already reset isLoading to false. The finally block on Line 43 then sets it again.

This is harmless but slightly redundant. Consider removing the finally block:

     const handleDelete = async () => {
-        try {
-            setIsLoading(true)
-            await onDeleteElement()
-            setOpen(false)
-        } finally {
-            setIsLoading(false)
-        }
+        setIsLoading(true)
+        await onDeleteElement()
+        setOpen(false)
     }

Alternatively, keep the finally for explicit cleanup if onDeleteElement throws before setOpen(false) is reached.

app/components/graph/UploadGraph.tsx (1)

17-20: Simplify dialogOpen computation.

The useMemo adds overhead for a simple ternary. Since open and internalOpen are both primitives, React's normal rendering would handle this efficiently.

-    const dialogOpen = useMemo(
-        () => (isControlled ? (open as boolean) : internalOpen),
-        [isControlled, open, internalOpen]
-    )
+    const dialogOpen = isControlled ? open : internalOpen
app/graph/DataTable.tsx (2)

63-94: Consider debouncing resize measurements.

The measureValueOverflow function runs on every resize event and ResizeObserver callback. For components with many attributes, this could cause performance issues.

+import { useMemo, useCallback, useLayoutEffect, useRef, useState, Fragment, useEffect, useContext } from "react"
+
+// Add a simple debounce helper or use lodash
+const debounce = <T extends (...args: any[]) => void>(fn: T, ms: number) => {
+    let timeoutId: ReturnType<typeof setTimeout>
+    return (...args: Parameters<T>) => {
+        clearTimeout(timeoutId)
+        timeoutId = setTimeout(() => fn(...args), ms)
+    }
+}

// Then wrap measureValueOverflow:
+const debouncedMeasure = useMemo(
+    () => debounce(measureValueOverflow, 100),
+    [measureValueOverflow]
+)

96-104: Missing cleanup for debounced handler if implemented.

If debouncing is added, ensure the debounced function is cancelled on unmount and the timeout is cleared.

.github/workflows/playwright.yml (1)

272-283: Consider adding a failure exit if app doesn't start.

The startup loop doesn't exit with an error if the app fails to start within 30 seconds. Tests will run against an unavailable app, potentially causing confusing failures.

          for i in {1..30}; do
            if curl -f http://localhost:3000 > /dev/null 2>&1; then
              echo "App is ready"
              break
            fi
+           if [ $i -eq 30 ]; then
+             echo "App failed to start within 30 seconds"
+             exit 1
+           fi
            sleep 1
          done
app/api/validate-body.ts (2)

201-207: Consider stricter typing for messages array.

Using z.any() for the messages array bypasses type validation. If the message structure is known (e.g., OpenAI chat format), consider defining it explicitly for better runtime validation.

+ const chatMessage = z.object({
+   role: z.enum(["user", "assistant", "system"]),
+   content: z.string(),
+ });
+
  export const chatRequest = z.object({
-   messages: z
-     .array(z.any(), {
+   messages: z
+     .array(chatMessage, {
        required_error: "Messages are required",
        invalid_type_error: "Invalid Messages",
      })
      .min(1, "Messages are required"),

229-282: Login schema mixes creation and connection concerns.

The login schema combines connection params (host, port, tls, ca) with token creation params (name, expiresAt, ttlSeconds). Consider splitting into separate schemas for clarity:

This is a nitpick - the current approach works if the API endpoint handles both concerns.

app/api/user/route.ts (1)

138-155: Consider handling partial deletion failures.

Promise.all will reject on the first failure, potentially leaving some users deleted and others not. If atomic behavior isn't required, Promise.allSettled could provide better visibility into which deletions succeeded or failed.

-await Promise.all(
-  users.map(async (user) => {
-    await connection.aclDelUser(user.username);
-  })
-);
+const results = await Promise.allSettled(
+  users.map((user) => connection.aclDelUser(user.username))
+);
+const failures = results.filter((r) => r.status === "rejected");
+if (failures.length > 0) {
+  throw new Error(`Failed to delete ${failures.length} user(s)`);
+}
app/schema/_page.tsx (1)

256-291: Consider memoizing getCurrentPanel to prevent unnecessary re-renders.

getCurrentPanel is recreated on every render. Since it depends on multiple state values and handlers, consider using useMemo or converting to inline JSX with proper memoization of child components.

-const getCurrentPanel = () => {
+const currentPanel = useMemo(() => {
   if (selectedElement) {
     return (
       <DataPanel
         object={selectedElement}
         setObject={handleSetSelectedElement}
         schema={schema}
         setLabels={setLabels}
       />
     )
   }
   // ... rest of the logic
   return null
-}
+}, [selectedElement, isAddNode, selectedElements, handleCreateElement, handleSetIsAddNode, handleSetIsAddEdge, schema, setLabels])

Then use {currentPanel} instead of {getCurrentPanel()} at line 356.

app/graph/Selector.tsx (1)

427-449: Fix formatting inconsistency in JSX.

There's irregular whitespace at lines 428 and 438 which appears to be a merge artifact.

 <div className="z-10 absolute bottom-4 right-8 flex gap-2">
-                                                                                          {
-                                                                historyQuery.counter ?
+  {historyQuery.counter && (
     <Button
       variant="Delete"
       data-testid="queryHistoryDelete"
       label="Delete"
       title="Remove selected query from history"
       onClick={handleDeleteQuery}
     />
-    : undefined
-}               <Button
+  )}
+  <Button
     ref={submitQuery}
e2e/tests/graph.spec.ts (1)

298-312: Consider adding animation wait to this similar test.

The @readonly Validate success when RO user attempts to execute ro-query test at lines 298-312 also calls selectGraphByName but doesn't have waitForCanvasAnimationToEnd(). If consistency is desired, consider adding it here as well.

 await graph.selectGraphByName(graphName);
+await graph.waitForCanvasAnimationToEnd();
 await graph.insertQuery("MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 10");
app/api/graph/[graph]/memory/route.ts (4)

7-9: Unused request parameter.

The request parameter is not used in this handler. Consider removing it or prefixing with underscore if the signature is required by Next.js.

 export async function GET(
-  request: NextRequest,
+  _request: NextRequest,
   { params }: { params: Promise<{ graph: string }> }
 ) {

18-27: Consider extracting shared version-check logic.

Calling getDBVersion() route handler directly works but is unconventional. If this version check is needed in multiple places, consider extracting the core logic to a shared utility function.


47-52: Add error logging for consistency.

The inner catch block doesn't log the error, unlike the pattern established in other routes (e.g., explain/route.ts). Adding console.error(err) would improve debugging.

     } catch (err) {
+      console.error(err);
       return NextResponse.json(
         { message: (err as Error).message },
         { status: 400 }
       );
     }

53-58: Outer catch missing error logging.

For consistency with the updated pattern in explain/route.ts, add console.error(err) here as well.

   } catch (err) {
+    console.error(err);
     return NextResponse.json(
       { message: (err as Error).message },
       { status: 500 }
     );
   }
.env.local.template (2)

8-8: Clarify ENCRYPTION_KEY requirements.

Consider adding a comment indicating the expected format/length for ENCRYPTION_KEY (e.g., 64-character hex string for AES-256) to help developers configure it correctly.


16-16: Add trailing newline.

Per static analysis, the file is missing a trailing newline. This is a POSIX convention and can prevent issues with some tools.

 PAT_FALKORDB_GRAPH_NAME=token_management
+
app/api/auth/DBVersion/route.ts (1)

4-31: DBVersion handler looks solid; consider documenting moduleList shape and NOPERM semantics

The overall pattern (getClient, NextResponse short‑circuit, inner/outer try/catch, and status mapping for "NOPERM") is consistent and technically sound. To make this more maintainable, consider adding a brief comment or small helper to clarify why result[0][1] and result[0][3] are the relevant indices and why "NOPERM" should map to HTTP 200, since both are a bit “magic” today.

app/api/schema/[schema]/duplicate/_route.ts (1)

3-52: Good move to centralized validation for schema duplication

Switching to validateBody(duplicateSchema, body) and returning a structured 400 on validation failure makes this endpoint much safer and easier to reason about. The rest of the flow (session check, source/target naming, 500 on outer failures) is coherent and in line with the other API routes.

app/api/graph/[graph]/duplicate/route.ts (1)

3-50: Graph duplication endpoint correctly migrated to validated request body

Using validateBody(duplicateGraph, body) and returning a 400 with a clear message on validation failure is a solid upgrade over ad‑hoc parsing. The rest of the handler (session handling, sourceNamecopy(graphId) call, and 500 fallback) is consistent with the surrounding API design.

app/api/graph/[graph]/count/nodes/route.ts (1)

1-77: SSE nodes count endpoint is consistent with edges count implementation

The SSE setup (TransformStream, encoder, abort handling, headers) and the runQuery‑based MATCH (n) RETURN count(n) as nodes flow are aligned with the existing edges count route and look correct. If you find yourself adding more SSE endpoints, consider extracting the common “count & stream result/error” pattern into a small helper to reduce duplication.

app/graph/Chat.tsx (1)

3-4: Chat request reshaping looks correct; consider typing the request body

The new flow that:

  • reads secretKey/model from BrowserSettingsContext,
  • constructs a JSON payload with messages, graphName, and optional model/key,
  • and posts it via fetch("/api/chat", …),

is coherent and aligns with a session‑based auth model instead of securedFetch. To keep things type‑safe, you could replace the any body with a small ChatRequest interface or inferred type, which would also help catch future changes to /api/chat’s contract at compile time. As per coding guidelines, this would improve TS correctness without changing runtime behavior.

Also applies to: 21-22, 89-113

app/api/schema/[schema]/_route.ts (1)

73-80: Schema creation via lazy initialization.

The RETURN 1 query triggers schema creation if it doesn't exist. This is an implicit side effect—consider adding a brief inline comment clarifying intent.

       const graph = client.selectGraph(schemaName);

+      // Trigger graph creation by executing a minimal query
       await graph.query("RETURN 1");
app/api/auth/encryption.ts (2)

7-19: Consider validating hex characters in ENCRYPTION_KEY.

The key length is validated, but if the key contains non-hex characters, Buffer.from(key, "hex") will silently produce a shorter or malformed buffer. This could lead to cryptographic errors at runtime.

 function getEncryptionKey(): Buffer {
   const key = process.env.ENCRYPTION_KEY;
   
   if (!key) {
     throw new Error("ENCRYPTION_KEY environment variable is required");
   }
   
   if (key.length !== 64) {
     throw new Error("ENCRYPTION_KEY must be 64 hexadecimal characters (32 bytes)");
   }
+
+  if (!/^[0-9a-fA-F]+$/.test(key)) {
+    throw new Error("ENCRYPTION_KEY must contain only hexadecimal characters");
+  }
   
   return Buffer.from(key, "hex");
 }

44-63: Consider adding validation for IV and authTag buffer lengths.

If the encrypted text is malformed (e.g., truncated IV or authTag), the decryption will fail with a less informative error. Validating expected lengths (12 bytes for IV, 16 bytes for authTag) would improve error messages.

   const iv = Buffer.from(parts[0], "hex");
   const authTag = Buffer.from(parts[1], "hex");
   const encrypted = parts[2];
+
+  if (iv.length !== 12) {
+    throw new Error("Invalid IV length in encrypted text");
+  }
+  if (authTag.length !== 16) {
+    throw new Error("Invalid auth tag length in encrypted text");
+  }
   
   const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
app/settings/tokens/PersonalAccessTokens.tsx (1)

214-220: Handle clipboard API errors.

navigator.clipboard.writeText can throw if clipboard access is denied. Wrap in try-catch for better UX.

-  const copyToClipboard = (text: string) => {
-    navigator.clipboard.writeText(text);
-    toast({
-      title: "Copied",
-      description: "Token copied to clipboard",
-    });
-  };
+  const copyToClipboard = async (text: string) => {
+    try {
+      await navigator.clipboard.writeText(text);
+      toast({
+        title: "Copied",
+        description: "Token copied to clipboard",
+      });
+    } catch {
+      toast({
+        title: "Error",
+        description: "Failed to copy to clipboard",
+        variant: "destructive",
+      });
+    }
+  };
app/graph/graphInfo.tsx (1)

34-53: Consider edge case when memory value is exactly 0.

Line 43 uses MemoryUsage.get("total_graph_sz_mb") || "<1" which will show "<1" when the value is 0 (falsy). If 0 MB is a valid value you want to display, use nullish coalescing instead.

-                                        <p className="truncate pointer-events-auto SofiaSans">{MemoryUsage.get("total_graph_sz_mb") || "<1"} MB</p>
+                                        <p className="truncate pointer-events-auto SofiaSans">{MemoryUsage.get("total_graph_sz_mb") ?? "<1"} MB</p>
app/graph/CreateElementPanel.tsx (1)

183-187: Type coercion could yield "object" for arrays.

typeof value returns "object" for arrays and null. Setting editType to "object" when the ValueType union is "string" | "number" | "boolean" may cause issues.

     const handleSetEditable = (key: string, value?: Value) => {
         setEditable(key)
         setEditVal(value || "")
-        setEditType(typeof value === "undefined" ? "string" : typeof value as ValueType)
+        const valueType = typeof value
+        const validType = valueType === "number" || valueType === "boolean" ? valueType : "string"
+        setEditType(typeof value === "undefined" ? "string" : validType)
     }
app/graph/selectGraph.tsx (1)

100-106: Handle potential undefined from getMemoryUsage.

If getMemoryUsage fails or returns an empty map, memoryMap.get("total_graph_sz_mb") returns undefined, which the fallback handles. However, if getMemoryUsage throws, the error isn't caught here.

     const loadMemory = useCallback((opt: string) =>
         async () => {
-            const memoryMap = await getMemoryUsage(opt, toast, setIndicator);
-            const memoryValue = memoryMap.get("total_graph_sz_mb") || '<1';
-
-            return `${memoryValue} MB`;
+            try {
+                const memoryMap = await getMemoryUsage(opt, toast, setIndicator);
+                const memoryValue = memoryMap.get("total_graph_sz_mb") || '<1';
+                return `${memoryValue} MB`;
+            } catch {
+                return "N/A";
+            }
         }, [toast, setIndicator])
app/api/auth/tokens/route.ts (3)

108-108: Unsafe non-null assertion.

Using fetchResult.tokens!.length assumes tokens is always defined when there's no error. Use nullish coalescing for safety.

-        count: fetchResult.tokens!.length,
+        count: fetchResult.tokens?.length ?? 0,

216-218: Type-unsafe password access.

Casting to any to access password bypasses TypeScript's type safety. The user type from getClient should be extended to include password when available.

-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      const password = (user as any).password || '';
+      const password = 'password' in user ? (user as { password?: string }).password || '' : '';

Alternatively, update the AuthenticatedUser interface to include password?: string.


230-249: Duplicate Cypher query construction pattern.

This is nearly identical to the query in credentials/route.ts. Consider extracting to a shared utility to reduce duplication and ensure consistent security handling.

app/graph/toolbar.tsx (2)

283-294: Semantic mismatch: variant="Delete" for Info button.

Using variant="Delete" for an informational button is confusing. Consider introducing a neutral or "Info" variant, or use a different existing variant.

                        <Button
                            data-testid={`elementCanvasInfo${label}`}
                            className="pointer-events-auto bg-background cursor-default border-primary"
-                           variant="Delete"
+                           variant="Secondary"
                            tooltipVariant="Primary"

295-319: Same variant concern for Add Node/Edge buttons.

The Add Node and Add Edge buttons also use variant="Delete", which is semantically incorrect for creation actions.

                        <Button
                            data-testid={`elementCanvasAddNode${label}`}
                            className="pointer-events-auto bg-background border-green-900"
-                           variant="Delete"
+                           variant="Secondary"
app/api/auth/[...nextauth]/options.ts (1)

274-301: Dynamic import adds latency; consider top-level import.

The getPasswordFromTokenDB is dynamically imported on each connection recreation. Since this is a standard code path, a top-level import would be cleaner and slightly more performant.

 import { isTokenActive } from "../tokenUtils";
+import { getPasswordFromTokenDB } from "../tokenUtils";

Then remove the dynamic import:

-          const { getPasswordFromTokenDB } = await import('../tokenUtils');
-          const password = await getPasswordFromTokenDB(payload.jti);
+          const password = await getPasswordFromTokenDB(payload.jti);
e2e/logic/POM/graphInfoPage.ts (2)

42-45: Avoid hardcoded waitForTimeout.

The 1000ms hardcoded timeout is a test smell. Consider waiting for a specific condition (e.g., element state change) rather than an arbitrary delay, which can lead to flaky tests.

  async isGraphInfoPanelContainerVisible(): Promise<boolean> {
-   await this.page.waitForTimeout(1000);
    return waitForElementToBeVisible(this.graphInfoNodesCount);
  }

123-133: Consider adding isGraphInfoEdgeButtonNotVisible for symmetry.

You have isGraphInfoNodeButtonNotVisible but no equivalent for edges. If edge visibility assertions are needed in tests, adding a symmetric method would maintain API consistency.

  async isGraphInfoEdgeButtonVisible(relationship: string): Promise<boolean> {
    return waitForElementToBeVisible(this.graphInfoEdgeButton(relationship));
  }
+
+ async isGraphInfoEdgeButtonNotVisible(relationship: string): Promise<boolean> {
+   return waitForElementToNotBeVisible(this.graphInfoEdgeButton(relationship));
+ }
}
app/api/auth/tokenUtils.ts (2)

50-51: Extract duplicated escapeString helper.

The escapeString function is defined twice (lines 51 and 115). Extract it as a module-level utility to avoid duplication.

+// Helper to escape strings for Cypher (FalkorDB doesn't support parameterized queries for all cases)
+const escapeString = (str: string) => str.replace(/'/g, "''");
+
 // Helper function to check if a token is active using FalkorDB PAT instance
 export async function isTokenActive(
   token: string
 ): Promise<boolean> {
   try {
     ...
-    // Helper to escape strings for Cypher (FalkorDB doesn't support parameterized queries)
-    const escapeString = (str: string) => str.replace(/'/g, "''");
     
     // Check if token exists in FalkorDB and is active

Also applies to: 114-115


126-128: Avoid logging sensitive identifiers in error messages.

The error messages include tokenId which could expose sensitive information in logs. Consider using a generic message or truncating/hashing the identifier for debugging purposes.

    if (!result || !result.data || result.data.length === 0) {
-     throw new Error(`Token not found or inactive: ${tokenId}`);
+     throw new Error("Token not found or inactive");
    }
    ...
    console.error("Error fetching password from Token DB:", error);
-   throw new Error(`Failed to retrieve password for token: ${tokenId}`);
+   throw new Error("Failed to retrieve password for token");
  }

Also applies to: 144-145

e2e/logic/POM/settingsTokensPage.ts (1)

82-84: Reduce reliance on hardcoded timeouts.

The waitFor helper wraps page.waitForTimeout, which is a test anti-pattern. While some delays may be unavoidable, consider using Playwright's built-in auto-waiting or explicit conditions like waitForSelector, waitForResponse, or expect.poll for more reliable tests.

app/graph/GraphView.tsx (1)

78-82: Complex condition for Metadata tab enablement.

The Metadata tab condition is quite verbose. Consider extracting to a named variable for clarity.

 const isTabEnabled = useCallback((tab: Tab) => {
     if (tab === "Table") return graph.Data.length !== 0
-    if (tab === "Metadata") return historyQuery.currentQuery && historyQuery.currentQuery.metadata.length > 0 && graph.Metadata.length > 0 && historyQuery.currentQuery.explain.length > 0
+    if (tab === "Metadata") {
+        const { currentQuery } = historyQuery
+        return currentQuery && currentQuery.metadata.length > 0 && graph.Metadata.length > 0 && currentQuery.explain.length > 0
+    }
     return true
 }, [graph, historyQuery.currentQuery])
e2e/logic/api/apiCalls.ts (1)

512-529: Generates new token for each listTokensAsUser call.

This method creates a new token via generateTokenAsUser for every list operation. For e2e tests this is acceptable, but be aware each call creates a new PAT that should ideally be cleaned up.

Consider adding a comment documenting this behavior:

 async listTokensAsUser(username: string, password: string): Promise<any> {
   try {
-    // First get a session/token by logging in
+    // Note: Creates a new PAT for each call - acceptable for e2e tests
+    // The created token should be cleaned up after test completion
     const loginResponse = await this.generateTokenAsUser(username, password);
app/graph/page.tsx (1)

241-244: Avoid mutating state array directly

selectedElements.push(selectedElement) mutates the state array directly. Consider creating a local copy to avoid potential React state management issues.

     const handleDeleteElement = useCallback(async () => {
-        if (selectedElements.length === 0 && selectedElement) {
-            selectedElements.push(selectedElement)
-        }
+        const elementsToDelete = selectedElements.length === 0 && selectedElement
+            ? [selectedElement]
+            : selectedElements;

-        await Promise.all(selectedElements.map(async (element) => {
+        await Promise.all(elementsToDelete.map(async (element) => {

Also update subsequent references from selectedElements to elementsToDelete within this callback.

…e functionality for refactoring and debugging purposes.
@Anchel123 Anchel123 changed the base branch from main to staging December 1, 2025 09:09
@Naseem77
Copy link
Contributor

Naseem77 commented Dec 1, 2025

@Anchel123 Too much unwanted changes, only require to disable the tab, and skip the tests.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
.github/workflows/playwright.yml (1)

212-290: Remove the commented-out job instead of leaving it commented; this also resolves the Gitleaks alert.

Commenting out an entire job leaves dead code in the repository and triggers security tooling (Gitleaks flagged the ENCRYPTION_KEY on line 270). While the learnings confirm the key is test-only, having it visible in source — even commented — is a code smell.

Two better options:

Option 1 (preferred if this is permanent): Delete the entire job and commit history. If token tests need to be re-added later, restore them from git history with proper context.

Option 2 (if temporary): Use GitHub Actions' conditional workflow syntax to disable the job without leaving commented code:

run-token-tests:
  name: Run token tests
  if: false  # Disabled until token testing is re-enabled
  needs: setup-dependencies
  # ... rest of job configuration ...

This approach keeps the configuration clear, allows easy re-enabling, and doesn't confuse static analysis tools.

Which approach aligns with your intent—permanent removal or temporary disabling pending re-enablement?

e2e/logic/api/responses/tokenResponse.ts (1)

1-19: Avoid duplicating token shape across interfaces

TokenDetailsResponse duplicates the exact shape of Token. To keep things in sync as the API evolves, consider aliasing instead of repeating fields.

-export interface TokenDetailsResponse {
-  token_id: string;
-  name: string;
-  createdAt: string;
-  expiresAt: string | null;
-  lastUsed: string | null;
-}
+export type TokenDetailsResponse = Token;
e2e/logic/api/apiCalls.ts (4)

434-444: Tighten typing for listTokens JSON parsing (optional)

The Promise<ListTokensResponse> return type is good. To avoid untyped json() results flowing through, you could explicitly assert the type at the call site.

   async listTokens(): Promise<ListTokensResponse> {
     try {
       const headers = await getAdminToken();
       const result = await getRequest(`${urls.api.tokenUrl}tokens`, headers);
-      return await result.json();
+      return (await result.json()) as ListTokensResponse;
     } catch (error) {
       throw new Error(
         `Failed to list tokens. \n Error: ${(error as Error).message}`
       );
     }
   }

458-468: Align getTokenDetails with its response type (optional)

Same idea here: you already declare Promise<TokenDetailsResponse>, so explicitly asserting the parsed JSON helps keep call sites strongly typed.

   async getTokenDetails(tokenId: string): Promise<TokenDetailsResponse> {
     try {
       const headers = await getAdminToken();
       const result = await getRequest(`${urls.api.tokenUrl}tokens/${tokenId}`, headers);
-      return await result.json();
+      return (await result.json()) as TokenDetailsResponse;
     } catch (error) {
       throw new Error(
         `Failed to get token details. \n Error: ${(error as Error).message}`
       );
     }
   }

519-541: Ensure temporary user tokens are cleaned up even when listing fails

Right now the cleanup (revokeToken(token_id)) runs only if the list request and json() both succeed. If either fails, the temporary token created by generateTokenAsUser can be left behind, which is undesirable even in tests.

You can make cleanup best-effort by tracking the temporary token id and revoking it in a finally block.

   async listTokensAsUser(username: string, password: string): Promise<any> {
-    try {
-      // First get a session/token by logging in
-      const loginResponse = await this.generateTokenAsUser(username, password);
-      const { token, token_id } = loginResponse;
-
-      // Use that token to list tokens
-      const result = await getRequest(
-        `${urls.api.tokenUrl}tokens`,
-        { Authorization: `Bearer ${token}` }
-      );
-      const response = await result.json();
-
-      // Clean up the temporary token
-      await this.revokeToken(token_id);
-
-      return response;
-    } catch (error) {
+    let temporaryTokenId: string | undefined;
+    try {
+      // First get a session/token by logging in
+      const loginResponse = await this.generateTokenAsUser(username, password);
+      const { token, token_id } = loginResponse;
+      temporaryTokenId = token_id;
+
+      // Use that token to list tokens
+      const result = await getRequest(
+        `${urls.api.tokenUrl}tokens`,
+        { Authorization: `Bearer ${token}` }
+      );
+      return await result.json();
+    } catch (error) {
       throw new Error(
         `Failed to list tokens as user ${username}. \n Error: ${(error as Error).message}`
       );
+    } finally {
+      if (temporaryTokenId) {
+        try {
+          await this.revokeToken(temporaryTokenId);
+        } catch {
+          // best-effort cleanup; ignore failures
+        }
+      }
     }
   }

Also, given prior feedback on this PR (“only require to disable the tab, and skip the tests”), consider whether you still need this multi-user helper now that token-based tests are disabled, or if it can be removed to keep the diff smaller.


543-569: Also clean up temporary tokens in revokeTokenAsUser on failure

revokeTokenAsUser has the same pattern: if the DELETE or json() call fails, the temporary token returned from generateTokenAsUser is never revoked.

A similar try/finally structure keeps test-generated tokens from lingering.

   async revokeTokenAsUser(
     username: string,
     password: string,
     tokenId: string
   ): Promise<any> {
-    try {
-      // First get a session/token by logging in
-      const loginResponse = await this.generateTokenAsUser(username, password);
-      const { token, token_id } = loginResponse;
-
-      // Use that token to revoke via DELETE
-      const result = await deleteRequest(
-        `${urls.api.tokenUrl}tokens/${tokenId}`,
-        { Authorization: `Bearer ${token}` }
-      );
-      const response = await result.json();
-
-      // Clean up the temporary token
-      await this.revokeToken(token_id);
-
-      return response;
-    } catch (error) {
+    let temporaryTokenId: string | undefined;
+    try {
+      // First get a session/token by logging in
+      const loginResponse = await this.generateTokenAsUser(username, password);
+      const { token, token_id } = loginResponse;
+      temporaryTokenId = token_id;
+
+      // Use that token to revoke via DELETE
+      const result = await deleteRequest(
+        `${urls.api.tokenUrl}tokens/${tokenId}`,
+        { Authorization: `Bearer ${token}` }
+      );
+      return await result.json();
+    } catch (error) {
       throw new Error(
         `Failed to revoke token as user ${username}. \n Error: ${(error as Error).message}`
       );
+    } finally {
+      if (temporaryTokenId) {
+        try {
+          await this.revokeToken(temporaryTokenId);
+        } catch {
+          // best-effort cleanup; ignore failures
+        }
+      }
     }
   }

Same as above, if token-based UI/tests are now disabled for this release, you may not need to keep this helper at all and could drop it to reduce PR scope.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79aaf45 and 5ac65a7.

📒 Files selected for processing (3)
  • .github/workflows/playwright.yml (1 hunks)
  • e2e/logic/api/apiCalls.ts (6 hunks)
  • e2e/logic/api/responses/tokenResponse.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

⚙️ CodeRabbit configuration file

Review TypeScript code for type safety, proper error handling, and Next.js best practices.

Files:

  • e2e/logic/api/responses/tokenResponse.ts
  • e2e/logic/api/apiCalls.ts
e2e/**/*

⚙️ CodeRabbit configuration file

Review test code for proper assertions, test coverage, and maintainability.

Files:

  • e2e/logic/api/responses/tokenResponse.ts
  • e2e/logic/api/apiCalls.ts
**/api/**/*

⚙️ CodeRabbit configuration file

Review API routes for security, error handling, and proper HTTP status codes.

Files:

  • e2e/logic/api/responses/tokenResponse.ts
  • e2e/logic/api/apiCalls.ts
🧠 Learnings (1)
📚 Learning: 2025-11-13T09:41:15.717Z
Learnt from: Naseem77
Repo: FalkorDB/falkordb-browser PR: 1208
File: .github/workflows/playwright.yml:264-264
Timestamp: 2025-11-13T09:41:15.717Z
Learning: The hardcoded ENCRYPTION_KEY value "a3f5d8c2b7e4f1a0c9d6e8b2f5a7c3d1e9f2a4b8c6d3e7f1a5b9c2d8e4f0a6b3" in .github/workflows/playwright.yml at the run-token-tests job is a test-only encryption key used for CI token tests and is not a production secret.

Applied to files:

  • .github/workflows/playwright.yml
🧬 Code graph analysis (1)
e2e/logic/api/apiCalls.ts (1)
e2e/logic/api/responses/tokenResponse.ts (2)
  • ListTokensResponse (9-11)
  • TokenDetailsResponse (13-19)
🪛 Gitleaks (8.29.1)
.github/workflows/playwright.yml

[high] 270-270: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Setup and cache dependencies
🔇 Additional comments (2)
e2e/logic/api/apiCalls.ts (2)

33-33: Import of token response types looks correct

The new tokenResponse import is consistent with the existing response imports and keeps token-related typings centralized.


470-481: Using Promise.allSettled for bulk revoke is appropriate

Switching revokeAllUserTokens to Promise.allSettled is a good fit: you attempt to revoke all tokens in parallel and then fail only if any revocation is rejected, with a clear aggregated error message.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (8)
e2e/logic/api/responses/settingsResponses.ts (1)

1-7: Settings response typings are consistent with usage

The interfaces are straightforward and match how modifySettingsRole and getSettingsRoleValue consume them. If these are used beyond tests later, consider more descriptive property naming than a generic config field for long-term clarity.

e2e/logic/api/responses/userResponses.ts (1)

1-15: User response types look good; consider reusing common shapes

The user-related response interfaces are clear and type-safe. Since CreateUsersResponse and DeleteUsersResponse share the same { message: string } structure, you could optionally introduce a shared MessageResponse to DRY things up if you add more similar endpoints later.

e2e/logic/api/responses/schemaResponses.ts (1)

1-14: Schema response interfaces are coherent; align removeSchema return type

The schema response types are coherent and match their likely payloads. In ApiCalls.removeSchema you currently use Promise<RemoveGraphResponse> even though RemoveSchemaResponse exists with the same shape; switching that method to Promise<RemoveSchemaResponse> would avoid semantic confusion and better reflect intent.

e2e/logic/api/responses/graphResponses.ts (1)

1-54: Graph response types are reasonable; tighten naming and any-usage if possible

The graph response interfaces look structurally sound for the e2e layer. Two optional cleanups to consider:

  • Rename DuplicateGraphresponseDuplicateGraphResponse to match the other interface names and typical PascalCase conventions.
  • Instead of a file-level @typescript-eslint/no-explicit-any disable, you could scope it to RunQueryResponse.data or replace any with a more precise generic/union once the query result shape is better understood.

Neither is blocking, but they’ll improve consistency and lint hygiene.

e2e/logic/api/apiCalls.ts (4)

444-454: Strongly typing token list/detail responses is a good step; consider finishing the set

Updating listTokens and getTokenDetails to return ListTokensResponse / TokenDetailsResponse removes a lot of any from the token surface and will make tests safer to refactor. For consistency, you may also want to eventually introduce concrete response types for generateToken and revokeToken so all token helpers have well-defined shapes.

Also applies to: 468-478


482-490: Parallel token revocation is fine; error detail could be richer

Using a parallel “all-settled” style revocation ensures you attempt to revoke every token even if some calls fail, which is appropriate for cleanup. If you find these errors hard to debug in CI, you could enhance the thrown message to include which token IDs failed by zipping tokens with the settled results.


533-545: User token helpers now clean up temporary login tokens; add minimal guards

The new pattern of:

  • generating a short-lived token as the user,
  • using it to list or revoke tokens,
  • then revoking the temporary login token via its token_id,

is a solid way to avoid leaking credentials during e2e runs. To make failures easier to diagnose if the API shape ever changes, you might add a quick runtime guard that token and token_id are present before using them; otherwise you end up issuing DELETE requests with an undefined ID and get a less clear failure.

Also, given the scope of this PR around token handling, a concise title like:

  • test: refactor e2e token helpers and consolidate API response types

would capture the main impact.

Also applies to: 561-573


592-601: Align removeSchema return type with schema-specific interface

removeSchema currently returns Promise<RemoveGraphResponse>, even though RemoveSchemaResponse exists and has the same { message: string } payload. Switching to RemoveSchemaResponse here would better match the schema-focused behavior and the new schemaResponses.ts interfaces, and avoids coupling schema operations to graph response types.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ac65a7 and 4d69b6c.

📒 Files selected for processing (25)
  • e2e/logic/api/apiCalls.ts (6 hunks)
  • e2e/logic/api/responses/LoginResponse.ts (0 hunks)
  • e2e/logic/api/responses/addGraphResponse.ts (0 hunks)
  • e2e/logic/api/responses/addSchemaResponse.ts (0 hunks)
  • e2e/logic/api/responses/authResponses.ts (1 hunks)
  • e2e/logic/api/responses/changeGraphNameResponse.ts (0 hunks)
  • e2e/logic/api/responses/createUsersResponse.ts (0 hunks)
  • e2e/logic/api/responses/deleteUsersResponse.ts (0 hunks)
  • e2e/logic/api/responses/duplicateGraph.ts (0 hunks)
  • e2e/logic/api/responses/getGraphsResponse.ts (0 hunks)
  • e2e/logic/api/responses/getSchemaResponse.ts (0 hunks)
  • e2e/logic/api/responses/getSettingsRoleValue.ts (0 hunks)
  • e2e/logic/api/responses/getUsersResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphAttributeResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphCountResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphNodeResponse.ts (0 hunks)
  • e2e/logic/api/responses/graphResponses.ts (1 hunks)
  • e2e/logic/api/responses/logoutResponse.ts (0 hunks)
  • e2e/logic/api/responses/modifySettingsRoleResponse.ts (0 hunks)
  • e2e/logic/api/responses/removeGraphResponse.ts (0 hunks)
  • e2e/logic/api/responses/removeSchemaResponse.ts (0 hunks)
  • e2e/logic/api/responses/runQueryResponse.ts (0 hunks)
  • e2e/logic/api/responses/schemaResponses.ts (1 hunks)
  • e2e/logic/api/responses/settingsResponses.ts (1 hunks)
  • e2e/logic/api/responses/userResponses.ts (1 hunks)
💤 Files with no reviewable changes (19)
  • e2e/logic/api/responses/addSchemaResponse.ts
  • e2e/logic/api/responses/graphNodeResponse.ts
  • e2e/logic/api/responses/removeSchemaResponse.ts
  • e2e/logic/api/responses/createUsersResponse.ts
  • e2e/logic/api/responses/removeGraphResponse.ts
  • e2e/logic/api/responses/getUsersResponse.ts
  • e2e/logic/api/responses/graphCountResponse.ts
  • e2e/logic/api/responses/getSettingsRoleValue.ts
  • e2e/logic/api/responses/duplicateGraph.ts
  • e2e/logic/api/responses/getGraphsResponse.ts
  • e2e/logic/api/responses/changeGraphNameResponse.ts
  • e2e/logic/api/responses/getSchemaResponse.ts
  • e2e/logic/api/responses/runQueryResponse.ts
  • e2e/logic/api/responses/logoutResponse.ts
  • e2e/logic/api/responses/LoginResponse.ts
  • e2e/logic/api/responses/graphAttributeResponse.ts
  • e2e/logic/api/responses/deleteUsersResponse.ts
  • e2e/logic/api/responses/modifySettingsRoleResponse.ts
  • e2e/logic/api/responses/addGraphResponse.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

⚙️ CodeRabbit configuration file

Review TypeScript code for type safety, proper error handling, and Next.js best practices.

Files:

  • e2e/logic/api/responses/authResponses.ts
  • e2e/logic/api/responses/schemaResponses.ts
  • e2e/logic/api/responses/settingsResponses.ts
  • e2e/logic/api/responses/userResponses.ts
  • e2e/logic/api/responses/graphResponses.ts
  • e2e/logic/api/apiCalls.ts
e2e/**/*

⚙️ CodeRabbit configuration file

Review test code for proper assertions, test coverage, and maintainability.

Files:

  • e2e/logic/api/responses/authResponses.ts
  • e2e/logic/api/responses/schemaResponses.ts
  • e2e/logic/api/responses/settingsResponses.ts
  • e2e/logic/api/responses/userResponses.ts
  • e2e/logic/api/responses/graphResponses.ts
  • e2e/logic/api/apiCalls.ts
**/api/**/*

⚙️ CodeRabbit configuration file

Review API routes for security, error handling, and proper HTTP status codes.

Files:

  • e2e/logic/api/responses/authResponses.ts
  • e2e/logic/api/responses/schemaResponses.ts
  • e2e/logic/api/responses/settingsResponses.ts
  • e2e/logic/api/responses/userResponses.ts
  • e2e/logic/api/responses/graphResponses.ts
  • e2e/logic/api/apiCalls.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Setup and cache dependencies
🔇 Additional comments (2)
e2e/logic/api/responses/authResponses.ts (1)

1-13: Auth response typings are straightforward and consistent

The auth-related interfaces are minimal and consistent. They should integrate cleanly with the login and logout helpers in apiCalls.ts as long as the backend preserves this shape.

e2e/logic/api/apiCalls.ts (1)

15-43: Grouped response-type imports improve clarity

Centralizing the various response interfaces (graphResponses, userResponses, schemaResponses, settingsResponses, authResponses, tokenResponse) keeps this helper focused and self-documenting. This makes the return types of each method much clearer when scanning the class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants