Skip to content

refactor: untangle the codebase so a typical change lands in one place - #16

Merged
ismailkarsli merged 2 commits into
mainfrom
untangle-codebase
Nov 6, 2026
Merged

ismailkarsli merged 2 commits into
mainfrom
untangle-codebase

Conversation

@ismailkarsli

Copy link
Copy Markdown
Owner

Restructures the monorepo so that pieces which change together live together, and nothing stable imports something more volatile. Behaviour stays the same apart from the bug fixes below. Public exports stay.

Structure

  • Type engines: engine/ is a dependency-free kit, MySQL and SQLite share engine/family/, and PostgreSQL's lexer, types and facts live in postgres/engine. The engine <-> sql cycle is gone, and the root entry no longer loads the dialect engines.
  • Runtime: @typesql/core/adapter is now backed by per-dialect drivers plus connect, codegen, command and family modules, shared by the adapters, ORM bridges and CLI.
  • Migrations: each dialect is one record in migration/dialects.
  • Editor: the language server has one tokenizer and one dialect-facts record. Keyword lists come from the new @typesql/core/reserved.
  • Tooling and tests: package lists come from manifests (tools/packages.ts), Node and database versions are pinned once, and the runner records each public fact once. Tests that couldn't catch a bug were removed or merged.
  • Comments: those that only restated the code are gone.

Please review closely

These change what users see. All ship under patch intents unless noted, so please decide whether any should be bumped higher.

  • Col<"char"> in a hand-written PostgreSQL schema now means the one-byte "char" type, not char(n). Use "bpchar" for char(n).
  • $1::text[] now has nullable elements, so unnest($1::text[]) gives string | null. x = ANY(array) and ALL are nullable when the array can hold NULL. Both match what PostgreSQL returns, but existing code may need null handling.
  • executor(prisma) without a dialect throws for an unsupported provider instead of assuming PostgreSQL.
  • MySQL JSON object parameters are rejected at compile time.

Bugs fixed

Each has a scenario under tests/scenarios that was seen failing first.

  • PostgreSQL: a function call with a parameter in INSERT … VALUES or MERGE failed as "infinitely circular", and later statements in the file silently became any.
  • PostgreSQL: a cast parameter that accepts NULL could be typed non-null, depending on whether the select list held a $ or a comment.
  • MySQL and SQLite: multi-row INSERT … VALUES without a column list typed parameters against the wrong columns.
  • Migrations: BEGIN ATOMIC … END bodies were rejected, and begin or end used as names could reject a valid trigger or hide a later COMMIT.
  • typesql introspect on SQLite dropped tables named like a virtual table's shadow tables, such as docs_archive beside FTS table docs.
  • Drizzle on TiDB wrote values inside string literals: SELECT 'why7' AS note, ? AS id.
  • TiDB and PlanetScale serverless: a value could end its string on a server with NO_BACKSLASH_ESCAPES, which allowed SQL injection.

The change intents in .changeset/ list the rest.

Compatibility

SqlDriver.transaction keeps its 7.0 arguments and gains an optional trailing savepoint. serialTransaction and createTransactionalSql keep their 7.0 signatures. sqlite-declaration-emit and pg-declaration-emit compile drivers written for 7.0.

Known gaps

  • PostgreSQL's aggregate ORDER BY, window frame bounds and GROUPING SETS contents are parsed past, not checked. An unknown column there only fails at the database, and a parameter there is unknown. Documented in guide/limits.md.
  • Backslashes in values are still corrupted under NO_BACKSLASH_ESCAPES; injection is closed.
  • A SQLite trigger with UPDATE OF begin can still hide a later COMMIT.
  • Drizzle on PlanetScale v2 stays broken, because Drizzle itself passes parameters v2 rejects.
  • The final round of fixes, including the migration scanner changes, had no further independent review.

Verification

Run locally against the servers in tests/compose.yaml, without snapshot updates.

Check Before After
Scenarios 72 pass 112 pass
Instantiations per statement 8317 8234
Core main bundle, minzipped 2874 B 2863 B

Typecheck, lint (0 errors), knip, check:intents and the docs build all pass. Nothing ran against real TiDB or PlanetScale services: the Drizzle scenario uses the real TiDB client over a fake transport, and the literal-quoting scenario uses a fake client on real MySQL.

This branch also carries b6de994, the docs commit that was on local main but not yet on origin/main.

Say that @typesql/core is tiny (under 3 kB minified and gzipped) in the
READMEs, the npm description, the docs home page, the introduction and
quick start, and add a runtime size row to the comparison pages.
Move what changes together into one place, across all packages:

- engine/ is a dependency-free kit; MySQL and SQLite share engine/family/;
  PostgreSQL's own lexer, types and facts live in postgres/engine.
- The root entry no longer loads the dialect engines.
- @typesql/core/adapter splits into per-dialect drivers, connect, codegen,
  command and family modules, shared by adapters, ORM bridges and the CLI.
- Each migration dialect is one record in migration/dialects.
- The language server has one tokenizer and one dialect-facts record, and
  keyword lists come from @typesql/core/reserved.
- Package lists come from manifests (tools/packages.ts); Node and database
  versions are pinned once; the test runner records each public fact once.
- Comments that only restated the code are gone.

Bugs found on the way are fixed, each with a scenario: statement typing in
all dialects, migration statement scanning, SQLite introspection of tables
named like shadow tables, literals on the HTTP MySQL drivers. Change intents
describe each user-visible fix.
@ismailkarsli
ismailkarsli merged commit 129b384 into main Nov 6, 2026
3 checks passed
@ismailkarsli
ismailkarsli deleted the untangle-codebase branch November 6, 2026 21:46
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.

1 participant