Skip to content

Dedicated DB#2914

Draft
abnegate wants to merge 72 commits intomainfrom
feat-dedicated-db
Draft

Dedicated DB#2914
abnegate wants to merge 72 commits intomainfrom
feat-dedicated-db

Conversation

@abnegate
Copy link
Member

@abnegate abnegate commented Mar 12, 2026

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for Dedicated Databases with comprehensive management features including monitoring, backups, restorations, and Point-in-Time Recovery.
    • Introduced database creation wizard supporting multiple database types (TablesDB, DocumentsDB, Prisma, Dedicated).
    • Added NoSQL document editor with syntax highlighting and real-time validation.
    • New monitoring dashboard displaying metrics, connections, slow queries, and performance insights.
    • Implemented High Availability, cross-region failover, and read replica management.
  • Improvements

    • Enhanced backup configuration with automated policies and PITR windows.
    • Added database scaling, connection pooling, and performance tuning options.

ItzNotABug and others added 25 commits January 14, 2026 17:51
add: view selector for custom columns;
update; organize the filters for code-mirror;
update: disable search/find for now on the editor;
add: display names to view selector.
add: new sonners for error and save.
add: fuzzy search helper.
add: json5 parser, linter and remove custom one.
update: improve code editor's behaviour.
add: apply fuzzy suggestions.
update: faker for documentsDB.
fix: a bug on autocomplete + backspace adding excess commas.
add: duplicate content shortcut.
update: unsaved changes warning.
fix: responsive side spacing for header.
# Conflicts:
#	src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/spreadsheet.svelte
@appwrite
Copy link

appwrite bot commented Mar 12, 2026

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Failed Failed View Logs Preview URL QR Code

Tip

Every Git commit and branch gets its own deployment URL automatically

@abnegate abnegate marked this pull request as draft March 12, 2026 12:13
@abnegate abnegate changed the title Feat dedicated db Dedicated DB Mar 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Walkthrough

This pull request introduces comprehensive support for dedicated databases (Prisma, shared, and dedicated types) across the Appwrite console. Changes include a new dedicated databases SDK module exporting 45+ methods and 30+ types, refactoring database terminology from table-centric to entity-centric architecture to support both traditional tables and NoSQL documents, adding dedicated database management pages and settings components (backups, monitoring, HA, cross-region, read replicas, etc.), introducing a CodeMirror 6-based JSON editor with syntax highlighting and validation, new collection and document management pages, and updates to dependencies, stores, and navigation to accommodate multiple database types throughout the application.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-dedicated-db

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: 7

Note

Due to the large number of review comments, Critical 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 (10)
src/lib/helpers/faker.ts (1)

123-145: ⚠️ Potential issue | 🟠 Major

Don't infer the schema-less path from filteredColumns.length === 0.

At Lines 123-145, this folds together "no fields were supplied" and "fields were supplied but none are writable." The latter can happen for tables with only relationships or temporarily unavailable columns, and this path then fabricates name/email/etc. keys that do not exist in that schema. Please make the default-record mode explicit instead of deriving it from the filtered result.

💡 If undefined is the intended schema-less signal
     const filteredColumns =
         field?.filter(
             (column) => column.type !== 'relationship' && column.status === 'available'
         ) ?? [];
+    const useDefaultRecord = field === undefined;
@@
-        if (filteredColumns.length === 0) {
+        if (useDefaultRecord) {
             record = generateDefaultRecord(id);
         } else {
             record = { $id: id };
             for (const column of filteredColumns) {
                 record[column.key] = generateValueForField(column);
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/helpers/faker.ts` around lines 123 - 145, The code incorrectly treats
"no writable columns" the same as "schema-less" by checking
filteredColumns.length === 0; change the branch to explicitly detect schema-less
input (check if field === undefined) and only call generateDefaultRecord(id) in
that case, otherwise build record = { $id: id } and populate with
generateValueForField(column) for each column in filteredColumns; update any
related type assumptions around record and retain the existing ID.unique() /
count loop and filteredColumns computation.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte (1)

263-288: ⚠️ Potential issue | 🟡 Minor

Add !isDedicatedType guard to the bottom sheet button to prevent rendering empty menus for dedicated databases.

The button at line 253 that toggles openBottomSheet has no protection against dedicated database types. Since loadEntities() returns early for dedicated databases (line 92), sortedEntities would be empty, and clicking the button would render BottomSheet.Menu with no items. While the button appears only on entity screens (line 242), which shouldn't normally exist for dedicated databases, there's no explicit safeguard. Adding {#if !isDedicatedType} around the button or the on:click handler would prevent this edge case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte
around lines 263 - 288, Wrap the BottomSheet trigger and/or the BottomSheet.Menu
render with a guard that checks !isDedicatedType to avoid opening/rendering an
empty menu for dedicated DBs; specifically, ensure the click that toggles
openBottomSheet (and the BottomSheet.Menu usage that consumes sortedEntities) is
only reachable when isDedicatedType is false (or skip toggling when
isDedicatedType is true), since loadEntities returns early for dedicated DBs and
sortedEntities can be empty.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/store.ts (1)

125-128: ⚠️ Potential issue | 🟡 Minor

Add missing SortState import from parent store.

The SortState type is defined in src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts but not imported in this file. Without it, line 125 will fail TypeScript type-checking. The collection store at the same database level correctly imports it:

import type { SortState } from '$database/store';

Add this import statement to the table store.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/table-[table]/store.ts
around lines 125 - 128, The file defines sortState as writable<SortState> but
fails TypeScript checks because SortState is not imported; add a type-only
import for the SortState type from the parent database store module (the same
module used by the collection store) at the top of this table store file so the
sortState declaration compiles and references the correct SortState type.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/createPolicy.svelte (2)

242-269: ⚠️ Potential issue | 🔴 Critical

The single-plan “Daily backups” switch now points at the wrong preset.

In the non-backups flow, the new filtering leaves [daily, none], so $presetPolicies[1] resolves to none. Toggling this switch will submit the no-backup option instead of the daily policy.

Proposed fix
-        {`@const` dailyPolicy = $presetPolicies[1]}
+        {`@const` dailyPolicy = $presetPolicies.find((policy) => policy.id === 'daily')}
@@
-                    on:change={(event) => markPolicyChecked(event, dailyPolicy)}>
+                    on:change={(event) => dailyPolicy && markPolicyChecked(event, dailyPolicy)}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/backups/createPolicy.svelte
around lines 242 - 269, The current dailyPolicy constant uses a fixed index
($presetPolicies[1]) which points to the wrong preset after filtering; change
the lookup to find the daily preset by identity instead (e.g., locate the policy
where id/name/type equals "daily") and use that object when calling
markPolicyChecked from the InputSwitch (id="daily_backup"), also add a safe
guard if the find returns undefined so the UI doesn't crash; update references
to dailyPolicy in this component accordingly.

144-149: ⚠️ Potential issue | 🟡 Minor

Hoist selectedPolicyGroup above the reactive reset to fix use-before-declaration.

Line 148 assigns to selectedPolicyGroup before its declaration at line 182. Move the declaration above line 144 to resolve this Biome lint error.

+    let selectedPolicyGroup: string | null = null;
+
     $: if (isShowing) {
         resetFormVariables();
         showCustomPolicy = false;
         listOfCustomPolicies = [];
         selectedPolicyGroup = null;

Also, change the type annotation from string to string | null since the variable is initialized and reset to null.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/backups/createPolicy.svelte
around lines 144 - 149, Move the declaration of selectedPolicyGroup so it
appears before the reactive block that resets state (the $: if (isShowing) { ...
} block) to avoid use-before-declaration; update its type annotation from string
to string | null since the code initializes and later resets it to null; ensure
references to selectedPolicyGroup inside resetFormVariables, showCustomPolicy,
listOfCustomPolicies, and the presetPolicies.update reactive code remain valid
after the move.
src/routes/(console)/project-[region]-[project]/databases/+page.svelte (2)

120-120: ⚠️ Potential issue | 🔴 Critical

Create component used but not imported.

The <Create> component at the end of the file is referenced but never imported, and handleCreate callback is also undefined. This appears to be leftover code from the previous modal-based flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/`(console)/project-[region]-[project]/databases/+page.svelte at
line 120, The file references the Create component and a handleCreate callback
but neither is defined; remove the leftover modal usage or reintroduce both
properly: either import the Create component and implement a handleCreate
function (e.g., to update local state or call the existing reload/refresh
routine) and pass the project prop, or delete the line "<Create bind:showCreate
on:created={handleCreate} project={data.project} />" and any related bind/show
state; ensure references to Create and handleCreate are consistently added or
removed so there are no unresolved symbols.

59-74: ⚠️ Potential issue | 🔴 Critical

Missing Tooltip import.

The Tooltip component is used but not imported. This will cause a runtime error.

Proposed fix
 import { Icon } from '@appwrite.io/pink-svelte';
+import { Tooltip } from '$lib/components';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/`(console)/project-[region]-[project]/databases/+page.svelte
around lines 59 - 74, The Tooltip component is used but not imported; add an
import for Tooltip at the top of the file (import Tooltip from the same
module/package you import Button/Icon from) so the <Tooltip> symbol is defined;
ensure the import is colocated with the other UI imports (where Button, Icon,
IconPlus are imported) to match existing patterns and avoid runtime errors.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/edit.svelte (1)

11-16: ⚠️ Potential issue | 🔴 Critical

Remove the duplicate Columns import.

This module declares Columns twice in the same scope (lines 11 and 16), which blocks TypeScript compilation and linting. Keep only the import from $database/store (line 16), as that aligns with the pattern used by other files in the columns directory.

Suggested fix
-import { type Columns, columnsOrder, databaseColumnSheetOptions } from '../store';
+import { columnsOrder, databaseColumnSheetOptions } from '../store';
 import { columnOptions, STRING_COLUMN_NAME, type Option } from './store';
 import { onMount } from 'svelte';
 import { Layout } from '@appwrite.io/pink-svelte';
 import { preferences } from '$lib/stores/preferences';
 import type { Columns } from '$database/store';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/edit.svelte
around lines 11 - 16, Remove the duplicate Columns type import by deleting
Columns from the first import line (the one importing "columnsOrder" and
"databaseColumnSheetOptions" from '../store') and keep only the import that
brings Columns from '$database/store'; ensure the remaining imports still
include columnsOrder, databaseColumnSheetOptions, columnOptions,
STRING_COLUMN_NAME, Option, onMount, Layout and preferences so there are no
unresolved symbols.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/create.svelte (1)

102-110: ⚠️ Potential issue | 🟠 Major

Run the trailing-dot cleanup after truncation.

slice(0, 36) can turn a previously valid string into one that ends with .. For a long name like aaaa... .b, this helper will auto-generate an ID that violates the backend rule you document above.

Suggested fix
         return str
             .toLowerCase()
             .replace(/[^a-z0-9\-_. ]+/g, '')
             .replace(/ /g, '_')
             .replace(/^-+/, '')
-            .replace(/\.+$/, '')
             .replace(/_{2,}/g, '_')
-            .slice(0, 36);
+            .slice(0, 36)
+            .replace(/\.+$/, '');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/create.svelte
around lines 102 - 110, The toIdFormat function currently slices the sanitized
string with .slice(0, 36) before removing trailing dots, which can create IDs
ending with '.' — modify to run the trailing-dot cleanup after truncation: in
function toIdFormat, keep all existing normalizations but move the
.replace(/\.+$/, '') (the trailing-dot removal) to after the .slice(0, 36) call
(and optionally re-run the duplicate-underscore collapse replace(/_{2,}/g, '_')
after slicing) so that any trailing dots introduced by truncation are removed
before returning the final ID.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (1)

209-227: ⚠️ Potential issue | 🔴 Critical

This toolbar block has malformed Svelte markup that won't compile.

Lines 209–227 mix two separate controls with broken tag nesting. The <Icon> component opened at line 218 is never closed before </Button> at line 225, and the </Tooltip> at line 228 has no matching opening tag in this block. Additionally, the preference key is set to entityHeaderExpanded here, but the same toggle uses tableHeaderExpanded elsewhere in the file (line 282), so the persisted state will be inconsistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
around lines 209 - 227, The toolbar contains malformed Svelte markup and
inconsistent preference keys: fix the Button/Icon nesting so the <Button> wraps
only its Icon children correctly and ensure every opened component/tag is
properly closed (inspect the Button and Icon usages around the on:click handlers
and the tooltip fragment), remove the stray unmatched </Tooltip> or add its
matching opening tag if a Tooltip is intended, and make the persisted toggle
consistent by using the same preference key used elsewhere (replace
preferences.setKey('entityHeaderExpanded', $expandTabs) with
preferences.setKey('tableHeaderExpanded', $expandTabs) to match the toggle at
tableHeaderExpanded); also ensure the import CSV Icon click handler
(showImportCSV) and disabled condition (hasColumns && hasValidColumns &&
!disableButton) remain attached to the correct Icon/Button after re-nesting.
🟡 Minor comments (26)
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/sonners/icons/CheckCircleDuotone.svelte-1-1 (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Hide this icon from assistive tech.

This component has no way to expose an accessible name, so screen readers will treat it as an unlabeled graphic. If it is purely decorative, mark the root SVG as hidden.

Suggested change
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
+<svg
+    xmlns="http://www.w3.org/2000/svg"
+    width="20"
+    height="20"
+    viewBox="0 0 20 20"
+    fill="none"
+    aria-hidden="true"
+    focusable="false">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/sonners/icons/CheckCircleDuotone.svelte
at line 1, The SVG in the CheckCircleDuotone.svelte component is decorative and
must be hidden from assistive tech; update the root <svg> element in
CheckCircleDuotone.svelte to include aria-hidden="true" (and add
focusable="false" for cross-browser behavior) so screen readers ignore the icon;
do this in the file where the <svg xmlns="http://www.w3.org/2000/svg" ...>
appears (the CheckCircleDuotone component) rather than attempting to provide an
accessible name.
src/lib/components/id.svelte-99-116 (1)

99-116: ⚠️ Potential issue | 🟡 Minor

Unused truncate prop in type definition.

The type definition includes truncate?: boolean; (line 113), but this prop is not destructured from $props() and is never used in the component. The truncateText action is always applied unconditionally.

Either remove truncate from the type definition if it's not needed, or destructure and use it to conditionally apply truncation.

Option 1: Remove unused type (if truncation should always apply)
 }: {
     value: string;
     event?: string | null;
     tooltipPortal?: boolean;
     tooltipDelay?: number;
     tooltipPlacement?: TooltipPlacement;
-    truncate?: boolean;
     copyText?: string;
     children: Snippet;
 } = $props();
Option 2: Implement conditional truncation (if truncate prop is needed)
 const {
     value,
     event = null,
     tooltipPortal = false,
     tooltipDelay = 0,
     tooltipPlacement,
     copyText,
+    truncate = true,
     children
 }: {

Then in the template, conditionally apply the action:

<span
    style:white-space="nowrap"
    style:overflow="hidden"
    style:word-break="break-all"
    use:truncateText={truncate ? {} : undefined}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/components/id.svelte` around lines 99 - 116, The props type declares
truncate?: boolean but truncate is not destructured from $props() nor used;
either remove truncate from the type annotation (if truncation should always
apply) or destructure truncate from $props() and use it to conditionally apply
the truncateText action. To implement the latter, add truncate to the
destructured props alongside value/event/tooltipPortal/etc. and change the
span's action usage (use:truncateText) to only pass the action when truncate is
truthy (e.g., use:truncateText={truncate ? {} : undefined}); if you choose
removal, delete the truncate?: boolean entry from the inline type.
src/lib/helpers/search.ts-59-59 (1)

59-59: ⚠️ Potential issue | 🟡 Minor

Handle limit: 0 predictably.

The truthy check turns limit = 0 into “no limit”, so callers asking for zero suggestions get the full list back instead.

🐛 Suggested fix
-    return limit && limit > 0 ? result.slice(0, limit) : result;
+    return limit === undefined ? result : result.slice(0, Math.max(0, limit));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/helpers/search.ts` at line 59, The current truthy check treats limit
= 0 as "no limit" and returns the full result; change the conditional to
explicitly detect a numeric limit and clamp it to >= 0 so callers requesting
zero suggestions get an empty array. Replace the truthy check around limit with
an explicit number check (e.g., typeof limit === 'number') and use Math.max(0,
limit) when slicing result (referencing the variables limit and result in this
file) so negative or zero values behave predictably.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/indexes/+page.svelte-52-57 (1)

52-57: ⚠️ Potential issue | 🟡 Minor

Explain why the create action is disabled.

When a collection has no fields, the only CTA is disabled with no guidance, so the user is blocked without a next step. Please add copy or a tooltip that tells them to create a column first.

Based on learnings, "In the Appwrite console databases UI, user-facing labels like "Columns" should remain unchanged even when internal terminology changes to "fields"."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/indexes/+page.svelte
around lines 52 - 57, The CTA is disabled when there are no fields but gives no
guidance; update the EmptySheetCards instance (props on the component used where
disabled={!data.collection.fields?.length}) to surface a user hint: when
disabled, show a tooltip or change the subtitle to "Create a column first" (or
similar) and preserve user-facing terminology "Columns" instead of "fields";
ensure the tooltip/subtitle appears only when data.collection.fields is empty
and keep the existing title "Create index" and onClick={toggle} unchanged.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sidesheet.svelte-67-67 (1)

67-67: ⚠️ Potential issue | 🟡 Minor

Scope the no-padding override to the sheet body only.

&.noContentPadding :global(section) will hit every descendant <section> inside the side sheet, including sections rendered by children. That makes noContentPadding strip spacing from nested content as well, not just the sheet container. Please target the specific Sheet body element instead of all descendant sections.

Also applies to: 192-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sidesheet.svelte
at line 67, The noContentPadding modifier on the sheet container is currently
targeting every descendant <section> via the selector like &.noContentPadding
:global(section), which strips padding from nested children; update the CSS to
scope the override to the sheet body only by changing those selectors to target
the sheet body element (e.g., .sheet-body or the more specific .sheet-container
> :global(section.sheet-body)) wherever used (including the occurrences around
lines 67 and 192-194), so only the sheet's own body loses padding and
nested/children sections remain unaffected.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte-166-190 (1)

166-190: ⚠️ Potential issue | 🟡 Minor

Don’t mount the mobile SideSheet when there is no editor content.

On small viewports this still renders a SideSheet even when noSqlEditor is undefined. If showEditorSideSheet flips to true, users can get an empty “Edit document” sheet, and the :has(.sheet-container:empty) rule will not reliably hide it because the sheet still renders wrapper/header markup.

Possible patch
-    <div class="no-sql-editor">
-        {`#if` !$isSmallViewport}
-            <div class="no-sql-editor desktop" style:height={spreadsheetHeight}>
-                {`@render` noSqlEditor?.()}
-            </div>
-        {:else}
-            <SideSheet
-                noContentPadding
-                bind:show={showEditorSideSheet}
-                submit={sideSheetOptions?.submit}
-                cancel={{
-                    onClick: () => {
-                        // fires state callback.
-                        showEditorSideSheet = false;
-                    }
-                }}
-                title={sideSheetOptions?.sideSheetTitle ?? 'Edit document'}>
-                {`@render` noSqlEditor?.()}
-
-                {`#snippet` topEndActions()}
-                    {`@render` sideSheetHeaderAction?.()}
-                {/snippet}
-            </SideSheet>
-        {/if}
-    </div>
+    {`#if` noSqlEditor}
+        <div class="no-sql-editor">
+            {`#if` !$isSmallViewport}
+                <div class="no-sql-editor desktop" style:height={spreadsheetHeight}>
+                    {`@render` noSqlEditor?.()}
+                </div>
+            {:else}
+                <SideSheet
+                    noContentPadding
+                    bind:show={showEditorSideSheet}
+                    submit={sideSheetOptions?.submit}
+                    cancel={{
+                        onClick: () => {
+                            showEditorSideSheet = false;
+                        }
+                    }}
+                    title={sideSheetOptions?.sideSheetTitle ?? 'Edit document'}>
+                    {`@render` noSqlEditor?.()}
+
+                    {`#snippet` topEndActions()}
+                        {`@render` sideSheetHeaderAction?.()}
+                    {/snippet}
+                </SideSheet>
+            {/if}
+        </div>
+    {/if}

Also applies to: 221-223

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte
around lines 166 - 190, The SideSheet is being mounted on small viewports even
when noSqlEditor is undefined; update the template to only render/mount
SideSheet when noSqlEditor is truthy: wrap the SideSheet block with a Svelte
{`#if` noSqlEditor} ... {/if} (or change the existing {:else} to {:else if
noSqlEditor}) so that SideSheet, its header/actions (sideSheetHeaderAction) and
bindings (bind:show={showEditorSideSheet}, submit={sideSheetOptions?.submit},
cancel handler) are only created when the editor content exists; apply the same
fix to the duplicate block referenced (lines 221-223).
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte-149-156 (1)

149-156: ⚠️ Potential issue | 🟡 Minor

Fire onOpen for an initially open editor sheet.

If this component mounts with showEditorSideSheet = true, previousShowEditorSideSheet already matches and the callback never runs. That skips sideSheetStateCallbacks.onOpen() for deep-linked or restored open states.

[suggested fix]

Possible patch
-    let previousShowEditorSideSheet = showEditorSideSheet;
+    let previousShowEditorSideSheet: boolean | undefined;

     $effect(() => {
-        if (showEditorSideSheet !== previousShowEditorSideSheet) {
+        if (previousShowEditorSideSheet === undefined) {
+            if (showEditorSideSheet) {
+                manageStateCallbacks(true);
+            }
+        } else if (showEditorSideSheet !== previousShowEditorSideSheet) {
             manageStateCallbacks(showEditorSideSheet);
-            previousShowEditorSideSheet = showEditorSideSheet;
         }
+        previousShowEditorSideSheet = showEditorSideSheet;
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte
around lines 149 - 156, The effect never calls manageStateCallbacks when the
component mounts with showEditorSideSheet already true because
previousShowEditorSideSheet is initialized to the same value; modify mounting
logic so that if showEditorSideSheet is true on mount you explicitly invoke
manageStateCallbacks(showEditorSideSheet) (which will trigger
sideSheetStateCallbacks.onOpen) before/after setting
previousShowEditorSideSheet, keeping the existing $effect for subsequent
changes; reference: previousShowEditorSideSheet, showEditorSideSheet, $effect,
manageStateCallbacks, and sideSheetStateCallbacks.onOpen.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/connectModal.svelte-140-143 (1)

140-143: ⚠️ Potential issue | 🟡 Minor

Disable the copy action when no connection string is available.

The content section already hides the URI input when database.connectionString is missing, but this button stays enabled and silently no-ops. Disable or hide it so the footer matches the available data.

Possible fix
-                <Button secondary on:click={copyConnectionString}>
+                <Button
+                    secondary
+                    disabled={!database.connectionString}
+                    on:click={copyConnectionString}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/connectModal.svelte
around lines 140 - 143, The "Copy Connection String" Button remains enabled even
when database.connectionString is absent; update the footer to either hide the
Button or set it disabled when database.connectionString is falsy. Locate the
Button element that triggers copyConnectionString and add a conditional render
or a disabled prop tied to a check like "database.connectionString" (and ensure
copyConnectionString no-ops safely if called). This keeps the UI consistent with
the hidden URI input and prevents a silent no-op action.
src/routes/(console)/project-[region]-[project]/databases/(assets)/mongo.svelte-11-13 (1)

11-13: ⚠️ Potential issue | 🟡 Minor

Use a meaningful alt here, or mark the image decorative.

alt="mongo-db artwork" is not a helpful accessible name. If this logo communicates the database type, use alt="MongoDB"; if it is purely decorative, use an empty alt instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/(assets)/mongo.svelte
around lines 11 - 13, The img tag using mongoDbImage currently has alt="mongo-db
artwork" which is not meaningful; update the alt attribute on that <img>
(referencing the mongoDbImage usage in the component) to either a descriptive
label like alt="MongoDB" if the logo conveys the database type, or to an empty
string alt="" if the image is purely decorative, and ensure any surrounding
markup (the div.custom-tag) does not duplicate necessary accessible text.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts-5-15 (1)

5-15: ⚠️ Potential issue | 🟡 Minor

Align the type definition with the actual initialization values.

context and entity are typed as optional (undefined allowed), but the store initializes both to null. This creates a type mismatch where the initialized value does not match the declared type. While TypeScript's strict null checks are currently disabled in this project, fixing this improves type safety and prevents issues if strict mode is enabled in the future.

Either allow null in the EntityColumnSuggestions type or initialize these fields to undefined.

Suggested fix
 export type EntityColumnSuggestions = {
     force: boolean;
     enabled: boolean;
     thinking: boolean;
-    context?: string | undefined;
+    context?: string | null;

     /* for safety when in tables page */
-    entity?: {
-        id: string;
-        name: string;
-    };
+    entity?:
+        | {
+              id: string;
+              name: string;
+          }
+        | null;
 };

Also applies to: 43–49

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts
around lines 5 - 15, The EntityColumnSuggestions type declares context?: string
and entity?: {id:string;name:string}, but the store initialization sets both to
null; update the code so the type aligns with initialization by either (A)
allowing null in the type (change to context?: string | null and entity?: { id:
string; name: string } | null) or (B) change the store initialization to use
undefined instead of null for context and entity; apply the same change for the
related fields referenced further down (the other EntityColumnSuggestions
instance at lines ~43–49).
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sheetOptions.svelte-87-87 (1)

87-87: ⚠️ Potential issue | 🟡 Minor

Guard against undefined column before calling isRelationship.

The column prop is now optional, but isRelationship(column) at line 119 is called without a null check. If column is undefined when rendering a header cell, this could cause unexpected behavior depending on how isRelationship handles undefined input.

🛡️ Proposed fix
             // hide sort options for relationship columns
-            if (isRelationship(column) && ['sort-asc', 'sort-desc'].includes(item?.action)) {
+            if (column && isRelationship(column) && ['sort-asc', 'sort-desc'].includes(item?.action)) {
                 return false;
             }

Also applies to: 119-121

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sheetOptions.svelte
at line 87, Guard against column being undefined before calling isRelationship:
update the header cell rendering logic that calls isRelationship(column) so it
first checks column (e.g., if (column && isRelationship(column)) { ... } or
const isRel = column ? isRelationship(column) : false) and handle the undefined
case (skip relationship-specific logic or use a safe fallback). Apply the same
guard to the other occurrences noted (lines around 119–121) to avoid calling
isRelationship with undefined and to preserve correct rendering when column is
optional.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/breadcrumbs.svelte-16-18 (1)

16-18: ⚠️ Potential issue | 🟡 Minor

Inconsistent optional chaining on organization.

Line 16 uses organization?.$id suggesting it could be undefined, but line 18 accesses organization.name without optional chaining. This inconsistency could cause a runtime error during page transitions when organization is undefined.

Based on learnings: In SvelteKit apps, shared layout components that use $derived(page.data.*) should use optional chaining when accessing properties that may not be present on all routes.

🛡️ Proposed fix
             {
                 href: resolveRoute('/(console)/organization-[organization]', {
                     organization: organization?.$id ?? project.teamId
                 }),
-                title: organization.name
+                title: organization?.name ?? ''
             },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/breadcrumbs.svelte
around lines 16 - 18, The code uses optional chaining for organization when
building the breadcrumb id (organization?.$id) but accesses organization.name
directly for title, which will throw if organization is undefined; update the
title assignment in the breadcrumbs (the symbol is title and the organization
object referenced in that component) to use optional chaining and a safe
fallback (e.g., organization?.name ?? an appropriate default) so title never
dereferences undefined during route transitions.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/settings/updateNetwork.svelte-46-52 (1)

46-52: ⚠️ Potential issue | 🟡 Minor

Add type guard for error handling.

The error parameter in catch blocks is typed as unknown in TypeScript strict mode. Accessing error.message directly may cause type errors or runtime issues if the caught value isn't an Error instance.

🛡️ Proposed fix
         } catch (error) {
+            const message = error instanceof Error ? error.message : 'An error occurred';
             addNotification({
-                message: error.message,
+                message,
                 type: 'error'
             });
             trackError(error, Submit.DatabaseUpdateNetwork);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/settings/updateNetwork.svelte
around lines 46 - 52, The catch block is using error.message directly though
caught values are unknown; update the catch in the try/catch around the network
update so it narrows the type (e.g., use a type guard like "error instanceof
Error" or an isError helper) and extract a safe message (fallback to
String(error) or a default) before calling addNotification and trackError;
ensure you still pass the original error value to trackError (trackError(error,
Submit.DatabaseUpdateNetwork)) but use the guarded message when calling
addNotification.
src/routes/(console)/project-[region]-[project]/databases/store.ts-26-43 (1)

26-43: ⚠️ Potential issue | 🟡 Minor

Add case for 'shared' database type in getDatabaseTypeTitle().

The shared database type is actively used throughout the codebase but lacks a case in this switch statement. Without it, shared databases fall through to the default case and display as "TablesDB" instead of their correct title.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/`(console)/project-[region]-[project]/databases/store.ts around
lines 26 - 43, The switch in getDatabaseTypeTitle is missing a case for the
'shared' DatabaseType, causing shared DBs to fall through to the default; add a
case 'shared' in getDatabaseTypeTitle (same place as the 'dedicated' case) that
reads database.engine (fallback to 'postgres'), maps engine to a human name
(postgres -> 'PostgreSQL', mysql -> 'MySQL', otherwise engine), and returns
`Shared ${engineName}` so shared databases display the correct title.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/usage/[[period]]/+page.ts-11-15 (1)

11-15: ⚠️ Potential issue | 🟡 Minor

Use spread operator pattern to match other usage pages.

The collection usage page uses a different return pattern than all other usage pages in the codebase. For consistency, use the spread operator pattern like the database, functions, storage, and auth usage pages.

♻️ Suggested fix
-    return sdk.forProject(params.region, params.project).documentsDB.getCollectionUsage({
+    return {
+        ...(await sdk.forProject(params.region, params.project).documentsDB.getCollectionUsage({
         databaseId: params.database,
         collectionId: params.collection,
         range: period
-    });
+        }))
+    };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/usage/[[period]]/+page.ts
around lines 11 - 15, The return currently calls
sdk.forProject(...).documentsDB.getCollectionUsage(...) directly; change it to
await the promise and return a new object using the spread operator to match
other usage pages. Specifically, call await sdk.forProject(params.region,
params.project).documentsDB.getCollectionUsage({ databaseId: params.database,
collectionId: params.collection, range: period }) and return { ...result }
(using the spread operator on the awaited result) so the page uses the same
spread-pattern as the database/functions/storage/auth usage pages.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/sonners/suggestions.svelte-21-29 (1)

21-29: ⚠️ Potential issue | 🟡 Minor

Use a platform-neutral shortcut hint.

This banner always tells the user to press ⌘ A, so Windows/Linux users get the wrong instruction. Reuse the same platform check as the command center or render Ctrl/Cmd + A.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/sonners/suggestions.svelte
around lines 21 - 29, The shortcut hint hardcodes "⌘ A" inside the
Layout.Stack/Badge block, which is incorrect on non-Mac platforms; update the
rendering logic in this component to detect the platform (reuse the same
platform check used by the command center or an existing util) and display
either "⌘ / A" for macOS or "Ctrl / A" for Windows/Linux (or render a combined
"Ctrl/Cmd + A" label) instead of the fixed "⌘" Badge and "A" Badge; modify the
badges inside the Layout.Stack (the Badges rendering the modifier and key) to
conditionally show the appropriate modifier symbol string so the UI shows
platform-neutral or platform-specific shortcut hints.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/inputs/displayName.svelte-33-35 (1)

33-35: ⚠️ Potential issue | 🟡 Minor

Confusing function name - hasChanged returns inverse of expected value.

The hasChanged() function returns isDisabled, which is true when there are no changes (or too many names). Callers would expect hasChanged() to return true when data has changed.

Proposed fix - rename or invert logic

Option 1: Rename to clarify intent:

-    export function hasChanged() {
-        return isDisabled;
+    export function canSubmit() {
+        return !isDisabled;
     }

Option 2: Return the actual "has changed" state:

     export function hasChanged() {
-        return isDisabled;
+        return symmetricDifference(names, getDisplayNames()).length > 0;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/inputs/displayName.svelte
around lines 33 - 35, The exported function hasChanged() currently returns
isDisabled (which is true when there are no valid changes), so invert its logic
or rename it to match intent: either change hasChanged() to return !isDisabled
so callers receive true when the data has actually changed, or keep the existing
return and rename hasChanged to something like isDisabledOrInvalid to reflect
the current behavior; update any callers of hasChanged() if you rename it to
avoid breaking references.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/sonners/error-bar.svelte-37-53 (1)

37-53: ⚠️ Potential issue | 🟡 Minor

Potential positioning issue on mobile viewport.

The media query sets left: 50% without a corresponding transform: translateX(-50%), so the element won't be horizontally centered—it will start at the 50% mark. Additionally, the default transform: translateX(60%) is overridden only partially (left/bottom/position change, but no transform reset).

Suggested fix to center on mobile
         `@media` (max-width: 768px) {
             left: 50%;
             bottom: 5%;
             position: absolute;
+            transform: translateX(-50%);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/sonners/error-bar.svelte
around lines 37 - 53, The .floating-action-bar style uses transform:
translateX(60%) globally and the mobile media query sets left: 50% without
resetting transform, causing mis-centering on small screens; update the mobile
rule for .floating-action-bar to reset or override transform (e.g., transform:
translateX(-50%)) so the element is horizontally centered when left: 50% is
applied, ensuring the media query fully overrides the desktop translateX(60%).
src/routes/(console)/project-[region]-[project]/databases/database-[database]/settings/updateExtensions.svelte-104-109 (1)

104-109: ⚠️ Potential issue | 🟡 Minor

Add type guard for error handling.

Same issue applies to the uninstall error handler.

🛡️ Proposed fix
         } catch (error) {
             addNotification({
-                message: error.message,
+                message: error instanceof Error ? error.message : 'Failed to uninstall extension',
                 type: 'error'
             });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/settings/updateExtensions.svelte
around lines 104 - 109, The catch block uses error.message directly; add a type
guard to handle non-Error throwables by checking if (error instanceof Error) and
using error.message, otherwise derive a safe string (e.g., String(error) or a
default like 'Unknown error') before calling addNotification and trackError;
update the uninstall error handler similarly and ensure trackError still
receives the original error object (or the guarded Error) when calling
trackError(error, Submit.DatabaseUninstallExtension).
src/routes/(console)/project-[region]-[project]/databases/database-[database]/settings/updateSqlApi.svelte-85-91 (1)

85-91: ⚠️ Potential issue | 🟡 Minor

Add type guard for error handling.

The error in the catch block is typed as unknown. Accessing error.message directly may fail at runtime if the caught value is not an Error object.

🛡️ Proposed fix
         } catch (error) {
             addNotification({
-                message: error.message,
+                message: error instanceof Error ? error.message : 'Failed to update SQL API settings',
                 type: 'error'
             });
             trackError(error, Submit.DatabaseUpdateSqlApi);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/settings/updateSqlApi.svelte
around lines 85 - 91, The catch block treats the caught value as an Error but
it's typed unknown; update the handler in the try/catch around addNotification
and trackError to first type-guard the caught value (use instanceof Error) and
extract a safe message (error.message) only for Error instances, otherwise
derive a fallback with String(error) or a default message, then pass either the
original Error to trackError or create a new Error from the fallback message;
ensure you update the references to addNotification, trackError, and
Submit.DatabaseUpdateSqlApi accordingly.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/settings/updateExtensions.svelte-67-72 (1)

67-72: ⚠️ Potential issue | 🟡 Minor

Add type guard for error handling.

Same pattern as other components - accessing error.message without checking the error type could fail at runtime.

🛡️ Proposed fix
         } catch (error) {
             addNotification({
-                message: error.message,
+                message: error instanceof Error ? error.message : 'Failed to install extension',
                 type: 'error'
             });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/settings/updateExtensions.svelte
around lines 67 - 72, The catch block in updateExtensions.svelte accesses
error.message directly; add a type guard to ensure the caught value is an Error
(or has a message) before using .message, and provide a safe fallback string
otherwise. Update the catch handler around the addNotification(...) and
trackError(...) calls: check using "if (error instanceof Error)" or "const msg =
typeof error === 'object' && error !== null && 'message' in error ? (error as
any).message : String(error)" then pass msg to addNotification and still call
trackError(error, Submit.DatabaseInstallExtension). Ensure you reference the
existing addNotification call, trackError function, and
Submit.DatabaseInstallExtension enum/value when making the change.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/settings/upgradeVersion.svelte-57-62 (1)

57-62: ⚠️ Potential issue | 🟡 Minor

Add type guard for error handling.

Same pattern as other components - accessing error.message without checking the error type.

🛡️ Proposed fix
         } catch (error) {
             addNotification({
-                message: error.message,
+                message: error instanceof Error ? error.message : 'Failed to upgrade database version',
                 type: 'error'
             });
             trackError(error, Submit.DatabaseUpgradeVersion);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/settings/upgradeVersion.svelte
around lines 57 - 62, The catch block in upgradeVersion.svelte reads
error.message directly; add a type guard to handle non-Error throwables: check
if (error instanceof Error) and use error.message, otherwise coerce to a string
(e.g., String(error) or a generic message) before calling addNotification and
pass the original error to trackError(…, Submit.DatabaseUpgradeVersion) as
before; update the catch surrounding symbols error, addNotification, and
trackError so the UI shows a safe message for all thrown values.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte-46-46 (1)

46-46: ⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug statement should be removed before merging to production.

🧹 Proposed fix
     async function onSelect(file: Models.File, localFile = false) {
         $isCollectionsJsonImportInProgress = true;
 
-        console.log(file, localFile);
-
         try {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte
at line 46, Remove the debug console.log call that prints file and localFile;
locate the statement "console.log(file, localFile);" in the page component
(+page.svelte) and delete it (or replace with a proper tidy logger call if
persistent logging is required) so no debug output remains in production.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/field/activity.svelte-26-56 (1)

26-56: ⚠️ Potential issue | 🟡 Minor

Add error handling to prevent infinite loading state.

If the SDK call fails, loading remains true indefinitely, leaving the user stuck on the skeleton loader.

🛡️ Proposed fix
     async function loadRecordLogs(event?: CustomEvent<number>) {
         loading = true;

         if (event) {
             offset = pageToOffset(event.detail, limit);
         }

         const { $databaseId: databaseId, entityId, $id: recordId } = toSupportiveRecord(record);

-        if (terminology.type === 'documentsdb') {
-            recordActivityLogs = await sdk
-                .forProject(page.params.region, page.params.project)
-                .documentsDB.listDocumentLogs({
-                    databaseId: databaseId,
-                    collectionId: entityId,
-                    documentId: recordId,
-                    queries: [Query.limit(limit), Query.offset(offset)]
-                });
-        } else {
-            recordActivityLogs = await sdk
-                .forProject(page.params.region, page.params.project)
-                .tablesDB.listRowLogs({
-                    databaseId: databaseId,
-                    tableId: entityId,
-                    rowId: recordId,
-                    queries: [Query.limit(limit), Query.offset(offset)]
-                });
-        }
-
-        loading = false;
+        try {
+            if (terminology.type === 'documentsdb') {
+                recordActivityLogs = await sdk
+                    .forProject(page.params.region, page.params.project)
+                    .documentsDB.listDocumentLogs({
+                        databaseId: databaseId,
+                        collectionId: entityId,
+                        documentId: recordId,
+                        queries: [Query.limit(limit), Query.offset(offset)]
+                    });
+            } else {
+                recordActivityLogs = await sdk
+                    .forProject(page.params.region, page.params.project)
+                    .tablesDB.listRowLogs({
+                        databaseId: databaseId,
+                        tableId: entityId,
+                        rowId: recordId,
+                        queries: [Query.limit(limit), Query.offset(offset)]
+                    });
+            }
+        } catch {
+            recordActivityLogs = null;
+        } finally {
+            loading = false;
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/field/activity.svelte
around lines 26 - 56, The loadRecordLogs function can leave loading=true if the
SDK call throws; wrap the SDK calls (the branches calling
sdk.forProject(...).documentsDB.listDocumentLogs and
sdk.forProject(...).tablesDB.listRowLogs) in a try/catch/finally so that loading
is set to false in finally, and in catch set recordActivityLogs to an empty
array and log the error (console.error or existing logger) to avoid an infinite
skeleton state; keep the existing offset logic (pageToOffset(event.detail,
limit)) and ensure the destructuring from toSupportiveRecord(record) remains
unchanged.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/+page.svelte-109-115 (1)

109-115: ⚠️ Potential issue | 🟡 Minor

Use an accessible name that matches the Documentation action.

This button is visibly labeled “Documentation”, but ariaLabel says create {entityLower.singular}. Screen readers will announce the wrong action here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/+page.svelte
around lines 109 - 115, The ariaLabel on the Button inside the slot "actions" is
incorrect (it says create {entityLower.singular}) and should match the visible
“Documentation” action; update the Button's ariaLabel prop to "Documentation"
(or the localized equivalent) so the accessible name aligns with the visible
text for the Button component used in this block.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte-168-170 (1)

168-170: ⚠️ Potential issue | 🟡 Minor

Keep this warning in "columns" terminology.

This toast is user-facing, so "fields" should stay internal here; otherwise the table UI mixes two labels for the same concept.

Based on learnings, 'In the Appwrite console databases UI, user-facing labels like "Columns" should remain unchanged even when internal terminology changes to "fields".'

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte
around lines 168 - 170, The toast message uses internal terminology "fields" but
should use the user-facing label "Columns"; update the addNotification call that
creates the warning (addNotification({... message: 'Cannot create rows: table
has no fields' })) to instead use "Columns" in the message (e.g., 'Cannot create
rows: table has no columns') so the UI remains consistent with the Appwrite
console terminology.

Comment on lines +221 to 228
deleteEntityDetails: async (orgId: string, entityId: string, databaseType?: string) => {
const dbType = databaseType ?? 'tables';
// remove from account preferences
const removeCustomTableColumns = updateAndSync((n) => {
n = ensureObjectProperty(n, 'tables');
delete n.tables[tableId];
n = ensureObjectProperty(n, dbType);
delete n.tables[entityId];
return n;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bug: Deletion always targets n.tables instead of using dbType.

The dbType variable is computed but then ignored on line 226 where deletion always uses n.tables[entityId]. This breaks the dual-path storage model for non-"tables" database types.

Proposed fix
         deleteEntityDetails: async (orgId: string, entityId: string, databaseType?: string) => {
             const dbType = databaseType ?? 'tables';
             // remove from account preferences
             const removeCustomTableColumns = updateAndSync((n) => {
                 n = ensureObjectProperty(n, dbType);
-                delete n.tables[entityId];
+                delete n[dbType][entityId];
                 return n;
             });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
deleteEntityDetails: async (orgId: string, entityId: string, databaseType?: string) => {
const dbType = databaseType ?? 'tables';
// remove from account preferences
const removeCustomTableColumns = updateAndSync((n) => {
n = ensureObjectProperty(n, 'tables');
delete n.tables[tableId];
n = ensureObjectProperty(n, dbType);
delete n.tables[entityId];
return n;
});
deleteEntityDetails: async (orgId: string, entityId: string, databaseType?: string) => {
const dbType = databaseType ?? 'tables';
// remove from account preferences
const removeCustomTableColumns = updateAndSync((n) => {
n = ensureObjectProperty(n, dbType);
delete n[dbType][entityId];
return n;
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/stores/preferences.ts` around lines 221 - 228, In
deleteEntityDetails, the computed dbType is ignored and the code always deletes
from n.tables; update the deletion to target the dynamic property using dbType
(i.e., delete from n[dbType][entityId]) so the removal respects non-"tables"
storage; keep the call to ensureObjectProperty(n, dbType) and use updateAndSync
as is, referencing the function name deleteEntityDetails and variables dbType,
updateAndSync, ensureObjectProperty and n.tables to locate and change the line.

Comment on lines +140 to +141
documentsDB: new DocumentsDB(clientProject),
dedicatedDatabases: new DedicatedDatabases(clientProject),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

DocumentsDB is used but not imported—this will cause a runtime error.

Line 26 shows /*DocumentsDB,*/ is still commented out, but line 140 attempts to instantiate new DocumentsDB(clientProject). This will throw a ReferenceError at runtime.

Proposed fix: Uncomment the DocumentsDB import
-    /*DocumentsDB,*/
+    DocumentsDB,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/stores/sdk.ts` around lines 140 - 141, The code instantiates
DocumentsDB (new DocumentsDB(clientProject)) but the DocumentsDB symbol is not
imported; restore/uncomment the DocumentsDB import where the other stores are
imported (remove the commented /*DocumentsDB,*/ and ensure the named import
matches the exported class name), then verify the import list also includes
DedicatedDatabases and that clientProject is the correct argument so the runtime
ReferenceError is resolved.

</ResponsiveContainerHeader>

{#if data.databases.total}
{@render containerHeader()}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

containerHeader is not defined — runtime error.

The {@render containerHeader()} call references a snippet that doesn't exist. This will cause a runtime error when data.databases.total is truthy.

You likely need to define the snippet or import/pass it from elsewhere.

🧰 Tools
🪛 ESLint

[error] 79-79: 'containerHeader' is not defined.

(no-undef)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/`(console)/project-[region]-[project]/databases/+page.svelte at
line 79, The template calls a non-existent Svelte snippet via {`@render`
containerHeader()}, causing a runtime error when data.databases.total is truthy;
fix by either defining a snippet function named containerHeader
(exported/available in this component) or replace the render call with the
correct snippet/import (or pass containerHeader in from the parent) so that
containerHeader() is a defined function/slot before it is invoked; look for the
{`@render` containerHeader()} usage and ensure the symbol containerHeader is
implemented or correctly imported/passed into this component.

Comment on lines +158 to +175
case 'dedicated': {
// Dedicated databases are created via the compute/databases endpoint
// with backend: 'appwrite'
const dedicatedParams = params as DedicatedDatabaseParams;
return (await baseSdk.dedicatedDatabases.create({
databaseId: dedicatedParams.databaseId,
name: dedicatedParams.name,
backend: 'appwrite',
engine: dedicatedParams.engine,
region: dedicatedParams.region,
tier: dedicatedParams.tier,
highAvailability: dedicatedParams.highAvailability,
backupEnabled: dedicatedParams.backupEnabled,
backupSchedule: dedicatedParams.backupSchedule,
backupRetentionDays: dedicatedParams.backupRetentionDays,
backupPitr: dedicatedParams.backupPitr,
pitrRetentionDays: dedicatedParams.pitrRetentionDays
})) as unknown as Models.Database;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Pass the dedicated type into this create call.

DedicatedDatabases.create() defaults a missing type to 'shared', so this branch currently provisions the wrong database class. Dedicated-database creation stays broken until this is explicit.

Suggested fix
                 case 'dedicated': {
                     // Dedicated databases are created via the compute/databases endpoint
                     // with backend: 'appwrite'
                     const dedicatedParams = params as DedicatedDatabaseParams;
                     return (await baseSdk.dedicatedDatabases.create({
                         databaseId: dedicatedParams.databaseId,
                         name: dedicatedParams.name,
                         backend: 'appwrite',
+                        type: 'dedicated',
                         engine: dedicatedParams.engine,
                         region: dedicatedParams.region,
                         tier: dedicatedParams.tier,
                         highAvailability: dedicatedParams.highAvailability,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case 'dedicated': {
// Dedicated databases are created via the compute/databases endpoint
// with backend: 'appwrite'
const dedicatedParams = params as DedicatedDatabaseParams;
return (await baseSdk.dedicatedDatabases.create({
databaseId: dedicatedParams.databaseId,
name: dedicatedParams.name,
backend: 'appwrite',
engine: dedicatedParams.engine,
region: dedicatedParams.region,
tier: dedicatedParams.tier,
highAvailability: dedicatedParams.highAvailability,
backupEnabled: dedicatedParams.backupEnabled,
backupSchedule: dedicatedParams.backupSchedule,
backupRetentionDays: dedicatedParams.backupRetentionDays,
backupPitr: dedicatedParams.backupPitr,
pitrRetentionDays: dedicatedParams.pitrRetentionDays
})) as unknown as Models.Database;
case 'dedicated': {
// Dedicated databases are created via the compute/databases endpoint
// with backend: 'appwrite'
const dedicatedParams = params as DedicatedDatabaseParams;
return (await baseSdk.dedicatedDatabases.create({
databaseId: dedicatedParams.databaseId,
name: dedicatedParams.name,
backend: 'appwrite',
type: 'dedicated',
engine: dedicatedParams.engine,
region: dedicatedParams.region,
tier: dedicatedParams.tier,
highAvailability: dedicatedParams.highAvailability,
backupEnabled: dedicatedParams.backupEnabled,
backupSchedule: dedicatedParams.backupSchedule,
backupRetentionDays: dedicatedParams.backupRetentionDays,
backupPitr: dedicatedParams.backupPitr,
pitrRetentionDays: dedicatedParams.pitrRetentionDays
})) as unknown as Models.Database;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/sdk.ts
around lines 158 - 175, The dedicated branch of the create flow is not passing
the database "type", causing DedicatedDatabases.create to default to 'shared';
update the dedicated branch (case 'dedicated') to include the explicit type
property (e.g., type: 'dedicated') in the object passed to
baseSdk.dedicatedDatabases.create using the DedicatedDatabaseParams values where
appropriate so the created database is the correct dedicated class.

Comment on lines +162 to +165
// Only register table searcher for non-dedicated databases
if (!isDedicatedType) {
$registerSearchers(tablesSearcher);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/routes/\(console\)/project-\[region\]-\[project\]/databases/database-\[database\]/+layout.svelte | head -200

Repository: appwrite/console

Length of output: 8800


Move the table searcher registration into a reactive statement to ensure isDedicatedType is defined.

The plain if statement at lines 162-165 executes once during component initialization, before the reactive assignment at line 34 has populated isDedicatedType. At that point, isDedicatedType is undefined, making !isDedicatedType evaluate to true, so tablesSearcher gets registered for all database types including dedicated ones. Wrap this in a reactive statement ($: if (!isDedicatedType) { ... }) to defer execution until isDedicatedType is defined.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/+layout.svelte
around lines 162 - 165, The table searcher registration runs too early because
isDedicatedType is populated reactively; change the plain if block to a Svelte
reactive statement so it runs after isDedicatedType is set: replace the existing
if (!isDedicatedType) { $registerSearchers(tablesSearcher); } with a reactive
statement using the `$:` label (i.e., `$: if (!isDedicatedType) {
$registerSearchers(tablesSearcher); }`) so registration only happens once
isDedicatedType is defined.

const params = page.params;
const project = page.data.project;
const database = page.data.database;
const organization = page.data.organization as Organization;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing import for Organization type.

The ESLint error confirms that Organization is not defined. Since Models is already imported from @appwrite.io/console, use Models.Organization instead.

🐛 Proposed fix
-        const organization = page.data.organization as Organization;
+        const organization = page.data.organization as Models.Organization;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const organization = page.data.organization as Organization;
const organization = page.data.organization as Models.Organization;
🧰 Tools
🪛 ESLint

[error] 11-11: 'Organization' is not defined.

(no-undef)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/breadcrumbs.svelte
at line 11, The type Organization is not imported; change the annotation to use
Models.Organization (i.e., cast page.data.organization as Models.Organization)
and ensure the existing import from '@appwrite.io/console' exposes Models
(update the import if necessary) so the variable declaration const organization
= page.data.organization as Models.Organization compiles without ESLint errors.

Comment on lines +69 to +72
await dedicatedSdk.updatePoolerConfig(database.$id, {
mode: poolerEnabled ? poolerMode : undefined,
defaultPoolSize: poolerEnabled ? poolSize : undefined
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for updatePoolerConfig definition in the SDK to verify expected parameters
ast-grep --pattern 'updatePoolerConfig($$$)'

# Also check the dedicatedDatabases SDK file for the method signature
rg -n 'updatePoolerConfig' --type ts

Repository: appwrite/console

Length of output: 127


🏁 Script executed:

sed -n '680,720p' src/lib/sdk/dedicatedDatabases.ts

Repository: appwrite/console

Length of output: 1421


🏁 Script executed:

# Find filterUndefined definition
rg -n 'filterUndefined' --type ts -A 5 | head -30

# Check if there's a separate enable/disable method or if the pooler API has an enabled field
rg -n 'pooler|Pooler' src/lib/sdk/dedicatedDatabases.ts | head -20

Repository: appwrite/console

Length of output: 2425


🏁 Script executed:

sed -n '246,260p' src/lib/sdk/dedicatedDatabases.ts

Repository: appwrite/console

Length of output: 369


🏁 Script executed:

rg -n 'Pooler|pooler' src/lib/sdk/dedicatedDatabases.ts | grep -i 'enable\|disable'

Repository: appwrite/console

Length of output: 42


updatePoolerConfig API is missing the enabled parameter to disable the pooler.

The PoolerConfig type includes an enabled: boolean field, but updatePoolerConfig only accepts mode, maxConnections, and defaultPoolSize parameters. When poolerEnabled is false, the component cannot send an explicit enabled: false to the backend—only undefined values are sent for mode and defaultPoolSize, which are then filtered out. The API method signature must include the enabled parameter to allow proper disabling of the pooler.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/settings/updatePooler.svelte
around lines 69 - 72, The updatePoolerConfig call is missing the required
enabled flag so the backend can receive enabled: false; update the API method
signature for updatePoolerConfig (on dedicatedSdk) and the PoolerConfig type to
accept an enabled?: boolean (or required boolean) parameter, then pass enabled:
poolerEnabled from the UI call (replace the current object with { enabled:
poolerEnabled, mode: poolerEnabled ? poolerMode : undefined, defaultPoolSize:
poolerEnabled ? poolSize : undefined }) so the backend can explicitly disable
the pooler when poolerEnabled is false.

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.

2 participants