Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions frontend/internal-packages/configs/eslint/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,6 @@ export function createBaseConfig(options = {}) {
"no-restricted-syntax": "off",
},
},
{
files: ["**/DropdownMenu/DropdownMenu.tsx", "**/parser.ts"],
rules: {
"no-restricted-syntax": "off",
},
},
{
files: ["**/trigger.config.ts", "**/vitest.config.ts"],
plugins: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,35 @@ export class PGliteInstanceManager {
* Execute SQL statements and return results with metadata
* Uses PostgreSQL parser to properly handle complex statements including dollar-quoted strings
*/
private isParseError(
result: PgParseResult,
): result is { error: { message: string } } {
return (result as any).error !== null
}
Comment on lines +65 to +69
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 | 🟠 Major

Fix type guard implementation to avoid unsafe type assertions.

The type guard uses as any, which defeats TypeScript's type safety and can lead to runtime errors. Additionally, checking error !== null without first verifying the property exists could throw if error is undefined.

Apply this diff to use proper type narrowing:

 private isParseError(
   result: PgParseResult,
 ): result is { error: { message: string } } {
-  return (result as any).error !== null
+  return 'error' in result && result.error != null
 }

Alternatively, if the property is always present but may be null/undefined:

 private isParseError(
   result: PgParseResult,
 ): result is { error: { message: string } } {
-  return (result as any).error !== null
+  return (result as { error?: { message: string } | null }).error != null
 }
🤖 Prompt for AI Agents
In frontend/internal-packages/pglite-server/src/PGliteInstanceManager.ts around
lines 65 to 69, the type guard uses an unsafe cast to any and only checks error
!== null which can throw if the property is missing; change the implementation
to first check that the 'error' property exists (using the 'in' operator or
typeof) and then test for non-null/undefined (error != null), e.g. return
'error' in result && result.error != null, so you avoid any type assertions and
properly narrow the type.


private isParseSuccess(
result: PgParseResult,
): result is { parse_tree: { stmts: any[] } } {
return (result as any).parse_tree?.stmts != null
}
Comment on lines +65 to +75
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

Verify PgParseResult type structure to ensure correct type guards.

The type guards assume a specific discriminated union structure for PgParseResult. Please verify the actual type definition to ensure the guards correctly match the type's structure.

Run the following script to examine the PgParseResult type definition:


🏁 Script executed:

#!/bin/bash
# Description: Find and display the PgParseResult type definition from the parser module

# Search for PgParseResult type definition
ast-grep --pattern 'type PgParseResult = $$$'

# Also search for interface definitions
ast-grep --pattern 'interface PgParseResult {
  $$$
}'

# Search for export statements
rg -nP --type=ts 'export.*PgParseResult' -A 5

Length of output: 2518


Fix type guard return types to properly narrow PgParseResult.

The logic in both guards is correct, but the return type annotations are incomplete. They only reflect the discriminating field (error or parse_tree) while omitting other required fields, breaking type safety for callers. The return types should preserve the full structure with narrowed error:

  • isParseError: return type should be result is PgParseResult & { error: NonNullable<PgParseResult['error']> }
  • isParseSuccess: return type should be result is PgParseResult & { error: null }

This ensures callers can still access stderr_buffer and other fields after the guard.

🤖 Prompt for AI Agents
In frontend/internal-packages/pglite-server/src/PGliteInstanceManager.ts around
lines 65 to 75, the two type guard return annotations are too narrow and drop
other PgParseResult fields; update them so isParseError returns result is
PgParseResult & { error: NonNullable<PgParseResult['error']> } and
isParseSuccess returns result is PgParseResult & { error: null }, keeping the
existing runtime checks but changing only the return type signatures so callers
retain access to fields like stderr_buffer and other PgParseResult properties
after narrowing.

Comment on lines +71 to +75
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 | 🟠 Major

Remove unsafe type assertion from type guard.

The type guard uses as any, which bypasses TypeScript's type checking. While the optional chaining provides some safety, proper type narrowing is more robust.

Apply this diff to use the in operator for type-safe narrowing:

 private isParseSuccess(
   result: PgParseResult,
 ): result is { parse_tree: { stmts: any[] } } {
-  return (result as any).parse_tree?.stmts != null
+  return 'parse_tree' in result && Array.isArray(result.parse_tree?.stmts)
 }
🤖 Prompt for AI Agents
In frontend/internal-packages/pglite-server/src/PGliteInstanceManager.ts around
lines 71 to 75, remove the unsafe "as any" assertion in the type guard and
replace it with proper property-in checks: first test whether "parse_tree" is in
result and that result.parse_tree is an object, then test whether "stmts" is in
result.parse_tree (and is an array if desired) using the "in" operator and
runtime checks; update the return type accordingly so the guard returns true
only when those properties exist and are correctly shaped, avoiding any use of
"as any".


private async executeSql(sqlText: string, db: PGlite): Promise<SqlResult[]> {
try {
const parseResult: PgParseResult = await pgParse(sqlText)

if (parseResult.error) {
if (this.isParseError(parseResult)) {
return [this.createParseErrorResult(sqlText, parseResult.error.message)]
}

const statements = this.extractStatements(
sqlText,
parseResult.parse_tree.stmts,
)
return await this.executeStatements(statements, db)
if (this.isParseSuccess(parseResult)) {
const statements = this.extractStatements(
sqlText,
parseResult.parse_tree.stmts,
)
return await this.executeStatements(statements, db)
}

return [this.createParseErrorResult(sqlText, 'No statements found')]
} catch (error) {
return await this.executeFallback(sqlText, db, error)
}
Expand Down
17 changes: 11 additions & 6 deletions frontend/packages/schema/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
export {
import {
detectFormat,
ProcessError,
parse,
type SupportedFormat,
setPrismWasmUrl,
supportedFormatSchema,
pgParse,
} from './parser/index.js'
import type { SupportedFormat, PgParseResult } from './parser/index.js'

// Export PostgreSQL-specific parser
export {
type PgParseResult,
parse as pgParse,
} from './parser/sql/postgresql/parser.js'
detectFormat,
ProcessError,
parse,
setPrismWasmUrl,
supportedFormatSchema,
pgParse,
}
export type { SupportedFormat, PgParseResult }
6 changes: 6 additions & 0 deletions frontend/packages/schema/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export {
supportedFormatSchema,
} from './supportedFormat/index.js'

// Export PostgreSQL-specific parser
export {
type PgParseResult,
parse as pgParse,
} from './sql/postgresql/parser.js'

export const parse = async (
str: string,
format: SupportedFormat,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,4 @@ export const DropdownMenuSeparator = () => {
return <Separator className={styles.separator} />
}

// Re-export for custom implementations
export {
ItemIndicator as DropdownMenuItemIndicator,
RadioItem as DropdownMenuPrimitiveRadioItem,
} from '@radix-ui/react-dropdown-menu'
DropdownMenuSeparator.displayName = 'DropdownMenuSeparator'
6 changes: 6 additions & 0 deletions frontend/packages/ui/src/components/DropdownMenu/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
// Re-export for custom implementations
export {
ItemIndicator as DropdownMenuItemIndicator,
RadioItem as DropdownMenuPrimitiveRadioItem,
} from '@radix-ui/react-dropdown-menu'

export * from './DropdownMenu'
Loading