Skip to content

fix: unbreak eslint . on main, and generate valid SQLite view DDL - #596

Merged
huyplb merged 1 commit into
mainfrom
claude/fervent-goodall-suen18
Nov 7, 2026
Merged

huyplb merged 1 commit into
mainfrom
claude/fervent-goodall-suen18

Conversation

@huyplb

@huyplb huyplb commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two unrelated fixes, both found by running the tooling rather than reading it.

1. npx eslint . was failing on main — this blocked the release gate

packages/server/src/internal/driver-install.ts
  335:7  error  Unused eslint-disable directive
         (no problems reported from 'security/detect-non-literal-fs-filename')

security/detect-non-literal-fs-filename does not cover rmSync, so the directive above it was dead and reportUnusedDisableDirectives failed the run. Confirmed red in CI on a8dd395 and 5cf9b39.

Three jobs run plain npx eslint . — build-gate's ESLint, dependency-security's ESLint security and release-gate's eslint-security, which the Release Draft needs — so all three were failing on one error.

Replaced the directive with a plain comment that keeps the reasoning and records why no directive belongs there.

2. SQLite view migrations were never executable

Adding a view to a SQLite target produced:

CREATE OR REPLACE VIEW v_customer_orders AS
CREATE VIEW v_customer_orders AS
SELECT ...
-> near "OR": syntax error

Two independent defects:

  • Doubled header. sqlite_master.sql is the whole CREATE VIEW … AS … text, and the provider stored it in definition. The generator treats definition as the query and emits the header itself, so it appeared twice. The SQL Server and DuckDB providers already strip their equivalent headers for exactly this reason — SQLite was the outlier.
  • No dialect overrides. SQLite has neither CREATE OR REPLACE VIEW nor ALTER VIEW, and overrode neither hook, so it fell through to the generator's Postgres-style default.

Changes

File What
packages/server/src/internal/driver-install.ts drop the dead eslint-disable
packages/db/src/cores/view-definition.ts new — stripCreateViewHeader, one tested implementation
packages/db/src/providers/sqlLite/sqlLite.provider.ts store only the query in definition
packages/sql/src/providers/sqlLite/sqlite.sql-dialect.ts add createViewStatement + alterViewStatement
3 test files 23 new tests

createViewStatement emits CREATE VIEW IF NOT EXISTS, matching how this dialect already emits indexes. alterViewStatement emits DROP VIEW IF EXISTS + CREATE VIEW, since views hold no data.

Notes for the reviewer

  • Why a shared helper. This would have been a third copy of the header-stripping regex, so it now lives in cores/view-definition.ts. The SQL Server and DuckDB providers keep their inline copies — I did not want to risk regressing two working providers in a bug fix. They can adopt the helper separately.
  • The detect-unsafe-regex suppression is measured, not assumed. Every quantifier is over a bounded negated class or \w, and the only optional group (the schema qualifier) requires a literal .. Checked on 50 KB pathological inputs — a 50,000-char identifier, a whitespace run, a./a repetition, an unclosed paren and an unterminated quote, none of which reach AS — all matched in under 1 ms.
  • The regex is forced onto one line on purpose: on a const … = continuation line the directive lands on the wrong line and reads as unused — the same failure this PR fixes.
  • Stale comment left alone. sql-generator.module.ts:1041 claims "all other dialects support CREATE OR REPLACE VIEW", which is now wrong for SQLite. Left as-is to keep this diff to the fix.

Test plan

Verified end to end against the seeded demo databases, not only in unit tests:

view statements generated:
   DROP VIEW IF EXISTS v_order_summary;
   CREATE VIEW IF NOT EXISTS v_customer_orders AS
   CREATE VIEW IF NOT EXISTS v_low_stock AS

view statements executed OK: 3, failed: 0
views on target after migration: v_customer_orders, v_low_stock
   v_customer_orders: queryable
   v_low_stock: queryable

Previously the added views failed outright.

  • npx vitest run — 6874 passed, 1 expected fail, 145 skipped (was 6851; +23 new tests)
  • npx eslint . — exit 0
  • npm run lint:security — exit 0
  • npm run typecheck — exit 0
  • bash scripts/seed/seed-all.sh sqlite then compare demo_a → demo_b, executing the generated migration against a copy of the target
  • Live non-SQLite dialects — not run; Docker has no daemon in this environment

Out of scope

Found while verifying, not touched here:

  • ResultsPanel.tsx:1047 react-hooks/exhaustive-deps warning (does not fail CI; eslint.config.js says each site needs a human decision)
  • splitSqlStatements cuts a BEGIN … END trigger body at its inner ; — likely the most consequential of these
  • the generator emits SQLite's reserved internal sqlite_autoindex_* names
  • SQLite rejects non-constant ADD COLUMN defaults (DEFAULT strftime(...))
  • isQuotedIdentifier emits a quote-wrapped identifier verbatim, which allows DDL injection from a source schema's column names — reported separately, needs a design decision


Generated by Claude Code


Note

Medium Risk
Changes affect SQLite schema introspection and generated migration DDL (a common apply path), but scope is limited to view handling and a lint-only server fix with regression tests.

Overview
Unblocks npx eslint . on main by removing a dead eslint-disable on rmSync in driver-install.ts (the rule does not apply there, so reportUnusedDisableDirectives failed CI).

SQLite view migrations are fixed in two places. The provider now runs stripCreateViewHeader on sqlite_master.sql so definition is only the query body, avoiding doubled CREATE VIEW … AS CREATE VIEW … when the generator re-emits the header. The SQLite dialect adds createViewStatement (CREATE VIEW IF NOT EXISTS) and alterViewStatement (DROP VIEW IF EXISTS + CREATE VIEW) instead of Postgres-style CREATE OR REPLACE VIEW / ALTER VIEW, which SQLite rejects.

Shared stripCreateViewHeader lives in packages/db/src/cores/view-definition.ts with broad unit tests; migration and dialect tests assert single-header output and unchanged Postgres behavior.

Reviewed by Cursor Bugbot for commit 13d7580. Bugbot is set up for automated code reviews on this repo. Configure here.

Two unrelated fixes, both found by running the tooling rather than reading it.

1. ESLint error on main (blocks the release gate)

   `npx eslint .` exited 1 on main:

     packages/server/src/internal/driver-install.ts
       335:7  error  Unused eslint-disable directive
              (no problems reported from 'security/detect-non-literal-fs-filename')

   That rule does not cover `rmSync`, so the directive above it was dead and
   `reportUnusedDisableDirectives` failed the run. Three CI jobs run plain
   `npx eslint .` — build-gate's "ESLint", dependency-security's "ESLint
   security" and release-gate's `eslint-security`, which the Release Draft
   `needs` — so all three were red. Replaced the directive with a plain
   comment that keeps the reasoning and says why no directive belongs there.

2. SQLite view migrations were never executable

   Adding a view to a SQLite target produced:

     CREATE OR REPLACE VIEW v_customer_orders AS
     CREATE VIEW v_customer_orders AS
     SELECT ...
     -> near "OR": syntax error

   Two independent defects:

   - `sqlite_master.sql` is the whole `CREATE VIEW … AS …` text, and the
     provider stored it in `definition`. The generator treats `definition` as
     the *query* and emits the header itself, so the header appeared twice.
     The SQL Server and DuckDB providers already strip their equivalent
     headers for exactly this reason; SQLite was the outlier. Rather than add
     a third copy of that regex, the stripping now lives in one tested helper
     (`cores/view-definition.ts`) that SQLite uses. The two existing providers
     keep their inline copies for now and could adopt it separately.

   - SQLite has neither `CREATE OR REPLACE VIEW` nor `ALTER VIEW`, and the
     dialect overrode neither hook, so it fell through to the generator's
     Postgres-style default. Added `createViewStatement` (`CREATE VIEW IF NOT
     EXISTS`, matching how this dialect emits indexes) and
     `alterViewStatement` (`DROP VIEW IF EXISTS` + `CREATE VIEW`, since views
     hold no data).

   The stale comment at sql-generator.module.ts:1041 claiming "all other
   dialects support CREATE OR REPLACE VIEW" is now wrong for SQLite; left
   alone to keep this diff to the fix.

Verified end to end against the seeded demo databases: comparing demo_a to
demo_b now emits `CREATE VIEW IF NOT EXISTS …`, all three view statements
execute against a real SQLite target, and both migrated views are queryable.
Previously the added views failed outright.

  npx vitest run    6874 passed, 1 expected fail, 145 skipped (was 6851)
  npx eslint .      exit 0 (one pre-existing exhaustive-deps warning remains)
  npm run lint:security  exit 0
  npm run typecheck      exit 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ehcTb7vYMCawV1ajxwfxB
@cursor

cursor Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 691ac68c-3b0f-43ff-981c-b06b3f5d40e8)

@huyplb
huyplb merged commit 358419e into main Nov 7, 2026
12 checks passed
@huyplb
huyplb deleted the claude/fervent-goodall-suen18 branch November 7, 2026 20:08
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