mcpField
S
sqlc-gen-moonbit-safety

sqlc-gen-moonbit-safety

github.com/mizchiTesting & quality41641 installsUpdated 1w ago
Repository

About sqlc-gen-moonbit-safety

A set of regression-check scripts and a test pattern for projects using sqlc-gen-moonbit with Cloudflare D1. It addresses BigInt binding hangs and mixed SQL placeholder styles, with checks suitable for code generation and CI.

Key features

  1. 01Wrap unsafe Int64 binds
  2. 02Verify generated bind wrapping
  3. 03Reject mixed SQL placeholder styles
  4. 04Provide a regression test pattern

Use cases

  1. 01Adding checks after sqlc code generation
  2. 02Investigating Cloudflare D1 1101 errors
  3. 03Running database verification in CI

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/mizchi/skills --skill sqlc-gen-moonbit-safety

After installing, type/sqlc-gen-moonbit-safetyin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

README.md
assets/scripts/
check-sql-placeholder-mix.ts
patch-int64-binds.ts
assets/tests/
patch-int64-binds.test.ts
references/
d1-bind-hang.md

Details

From the project README

sqlc-gen-moonbit safety gates

Two regression-defense scripts that any sqlc-gen-moonbit + Cloudflare D1 project should run on every codegen and in CI. Both come from real production bugs in mizchi/mnemo that took an afternoon each to debug.

When to invoke

Use when you're:

  • Bootstrapping a project that uses sqlc-gen-moonbit against Cloudflare D1.
  • Investigating a 1101 (Worker threw exception) that may be a D1 .bind() hang.
  • Adding a pnpm run db:verify gate (or equivalent) for sqlc codegen output.

What's in here

assets/scripts/patch-int64-binds.ts

Post-sqlc generate patch that wraps every @core.any(params.<Int64-field>) in src/db/gen/sqlc_queries.mbt with int64_bind_safe(...). The wrapper coerces JS BigInt → Number before D1 .bind().

Why this is needed: D1's .bind() does not handle BigInt. Passing a raw BigInt makes .run() / .all() never resolve. After ~30 s Cloudflare kills the worker with a 1101 Worker threw exception. sqlc-gen-moonbit emits Int64 fields as BigInt at the bind boundary by default.

Two modes:

  • --apply (default): rewrite the file in place. Wire into db:generate so it runs after every sqlc generate.
  • --verify: exit non-zero if any Int64 bind site is still unwrapped. Wire into CI / db:verify to catch a regression where someone hand-edited the gen file or the patch step was skipped.

Implementation notes:

  • Block-based parsing (source.split(/^(?=pub async fn )/m)). The earlier single-regex implementation used \{[^}]+\} as the body matcher, which silently truncated whenever the bind list contained (match params.x { Some(v) => @core.any(v); None => @core.null() }) — every bind after the inner } was skipped. The block split avoids that.
  • Handles both Int64 (direct @core.any(params.f)) and Int64? (the Some(v) => @core.any(v) arm gets wrapped too — v is still BigInt).
  • Friendly to fresh repos: skips cleanly when src/db/gen/sqlc_queries.mbt doesn't exist yet (so db:verify doesn't block before the first db:generate).
assets/scripts/check-sql-placeholder-mix.ts

Rejects -- name: statements in db/sqlite/query.sql that mix anonymous ? with sqlc.arg(...).

Why this is needed: sqlc-gen-moonbit compiles each sqlc.arg('name') to a fixed ?N and leaves anonymous ? for SQLite to auto-number ("max used + 1"). When both styles co-exist, a trailing anonymous ? can land on a number higher than the bind-array length. D1 then errors out, surfaced as a 1101.

Rule: pick one style per statement. The recommended style for sqlc-gen-moonbit is sqlc.arg(...) everywhere.

assets/tests/patch-int64-binds.test.ts

Reference test pattern: spins up a tmpdir project with synthetic sqlc_types.mbt + sqlc_queries.mbt, runs the script with node:child_process, asserts the bind wrap landed. Includes a regression case for the nested-brace bug so the block-based parser stays in place.

Suggested wiring

// package.json
{
  "scripts": {
    "db:generate": "sqlc generate && node scripts/patch-int64-binds.ts",
    "db:verify": "node scripts/patch-int64-binds.ts --verify && node scripts/check-sql-placeholder-mix.ts"
  }
}

db:verify should fire in CI (and ideally in the build pipeline) so a regen drift can't ship.

References

Source

The current versions live in mizchi/cloudflare-starterkit-mbt under scripts/. The starter's CI runs both as pnpm run db:verify. mnemo (the origin of these patterns) also runs them.

Comments

Markdown supported