Skip to content

fix(wasm): commit gosqlx.wasm to git — fixes production playground 404#423

Merged
ajitpratap0 merged 1 commit intomainfrom
fix/wasm-deploy-404
Mar 22, 2026
Merged

fix(wasm): commit gosqlx.wasm to git — fixes production playground 404#423
ajitpratap0 merged 1 commit intomainfrom
fix/wasm-deploy-404

Conversation

@ajitpratap0
Copy link
Owner

Problem

The playground at gosqlx.dev/playground shows "Failed to load SQL parser / HTTP 404" for every visitor. Confirmed via CDP (Chrome DevTools Protocol) live browser audit.

GET https://gosqlx.dev/wasm/gosqlx.wasm [404]
window.__wasmLoaded → undefined

Root Cause

Race condition between two deployment paths:

  1. website.yml CI — builds WASM via GOOS=js GOARCH=wasm go build, copies to website/public/wasm/, deploys to Vercel ✅
  2. Vercel GitHub integration — also fires on merge to main, deploys WITHOUT WASM (binary is gitignored) ❌

Vercel's auto-deploy runs after CI and overwrites the deployment, leaving the production site without the WASM binary.

Fix

  • Remove gosqlx.wasm from .gitignore — the binary now ships with every deploy regardless of which path runs
  • Add CI step to auto-commit rebuilt WASM when wasm/ Go source changes (with [skip ci] to prevent loops)
  • Bump website.yml permissions contents: readcontents: write to allow the auto-commit push

Verification

After merge, the website.yml deploy will run (touches website/**), build fresh WASM, and deploy. The Vercel auto-deploy will also include the committed WASM. Both paths now produce a working playground.

Test plan

  • https://gosqlx.dev/wasm/gosqlx.wasm returns HTTP 200
  • Playground loads and parses SQL successfully
  • CDP audit shows no 404 console errors

🤖 Generated with Claude Code

Root cause: Vercel's GitHub auto-deploy fires on merge to main but cannot
build the WASM binary (it's a Go build step not in Vercel's build). The
auto-deploy overwrites the website.yml CI deployment (which has WASM),
resulting in /wasm/gosqlx.wasm returning HTTP 404 in production.

Fix:
- Remove gosqlx.wasm from .gitignore so it ships with every deploy
- Add CI step to auto-commit rebuilt WASM when wasm/ source changes
- Bump website.yml permissions: read → write (needed for the git push)

The committed WASM (6.5MB, built 2026-03-16 from v1.13.0 source) is
current — no changes to wasm/ source since that build date.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Mar 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gosqlx-website Ready Ready Preview, Comment Mar 22, 2026 5:26am

@github-actions
Copy link

github-actions bot commented Mar 22, 2026

Claude finished @ajitpratap0's task —— View job


PR Review: WASM Deployment Fix

Todo List:

  • Read and analyze the workflow changes
  • Review .gitignore modifications
  • Examine the committed WASM binary
  • Assess security implications
  • Evaluate performance considerations
  • Check for potential bugs or edge cases
  • Provide comprehensive feedback

✅ Overall Assessment

This PR effectively solves the production 404 issue with a pragmatic approach. The root cause analysis is accurate and the solution is sound.

🔍 Detailed Review

Code Quality & Best Practices: Good ✅

Workflow Changes (.github/workflows/website.yml:42-52):

  • Smart conditional commits: Only commits when WASM actually changes (git diff --cached --quiet)
  • CI loop prevention: Uses [skip ci] to prevent infinite build loops
  • Proper git configuration: Sets bot identity for automated commits
  • Clear commit messages: Descriptive message indicates auto-rebuild

Gitignore Changes (website/.gitignore:43-45):

  • Clear documentation: Excellent comments explaining the reasoning
  • Intentional approach: Removes WASM from ignore list with explicit justification

Security Concerns: Low Risk ✅

  • Binary verification: The committed binary is a valid WASM file (6.5MB, WebAssembly MVP format)
  • Standard runtime: wasm_exec.js is the official Go WASM runtime (Copyright 2018 The Go Authors)
  • Controlled source: WASM is built from the project's own Go source code
  • No secrets exposure: No sensitive data in the workflow changes

Minor consideration: Committing binaries increases repository size, but this is acceptable for critical production assets.

Performance Considerations: Excellent ✅

  • Smart caching: Service worker with hash-based cache invalidation (package.json:12)
  • Efficient workflow: Only triggers on relevant path changes (website/**, wasm/**, etc.)
  • Conditional deployment: Workflow uses concurrency controls and proper environments

Potential Issues & Edge Cases: Minimal Risk ✅

Identified edge case:

  • If the WASM build step fails, the workflow could fail silently in the commit step
  • Mitigation: The CI will fail if make build fails, which is appropriate

Recommendation: Consider adding explicit error handling:

- name: Build WASM
  run: |
    cd wasm && make build || exit 1
    mkdir -p ../website/public/wasm
    cp playground/gosqlx.wasm ../website/public/wasm/
    cp playground/wasm_exec.js ../website/public/wasm/

Test Coverage: Adequate for Deployment Fix ✅

While this PR doesn't include automated tests, the deployment fix is verifiable through:

  • Manual verification of WASM file availability at /wasm/gosqlx.wasm
  • Functional testing of the playground
  • The PR includes a clear test plan

🎯 Recommendations

  1. Document the deployment architecture: Consider adding a brief comment in the workflow explaining the dual-deployment scenario
  2. Monitor repository size: The 6.5MB binary will grow the repo over time - consider periodic cleanup if needed
  3. Add deployment health check: Consider adding a step to verify the deployed WASM is accessible

📊 Files Impact Analysis

  • website.yml: +13/-1 lines - Well-structured workflow enhancement
  • website/.gitignore: +2/-2 lines - Clear intent with documentation
  • gosqlx.wasm: +6.5MB - Production-critical binary asset

Verdict: This is a well-thought-out solution that addresses the root cause effectively. The implementation follows best practices and includes appropriate safeguards. Recommend approval.


@ajitpratap0 ajitpratap0 merged commit 04f5210 into main Mar 22, 2026
21 checks passed
ajitpratap0 pushed a commit that referenced this pull request Mar 22, 2026
…on main

The wasm auto-commit step from PR #423 fails with GH013 (branch protection
rules prevent direct pushes to main, even from CI). This aborts the entire
deploy pipeline before Vercel runs.

The WASM is now committed in git (PR #423), so the auto-commit step is
redundant for normal deploys. When wasm/ source changes, rebuild and commit
manually. Revert permissions back to contents: read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ajitpratap0 added a commit that referenced this pull request Mar 22, 2026
…on main (#424)

The wasm auto-commit step from PR #423 fails with GH013 (branch protection
rules prevent direct pushes to main, even from CI). This aborts the entire
deploy pipeline before Vercel runs.

The WASM is now committed in git (PR #423), so the auto-commit step is
redundant for normal deploys. When wasm/ source changes, rebuild and commit
manually. Revert permissions back to contents: read.

Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
ajitpratap0 added a commit that referenced this pull request Mar 22, 2026
…und CTA, WCAG, RSS (#425)

* fix(ci): remove auto-commit WASM step — blocked by branch protection on main

The wasm auto-commit step from PR #423 fails with GH013 (branch protection
rules prevent direct pushes to main, even from CI). This aborts the entire
deploy pipeline before Vercel runs.

The WASM is now committed in git (PR #423), so the auto-commit step is
redundant for normal deploys. When wasm/ source changes, rebuild and commit
manually. Revert permissions back to contents: read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(website): Phase 1 marketing — star button, social share, playground CTA, WCAG, RSS

- Hero: live GitHub star button with client-side count fetch (GitHubStarButton)
- Hero: fix WCAG contrast — zinc-500/emerald-400/70 → zinc-400/emerald-400
- Blog post: X/Twitter, LinkedIn, HN share buttons after article content
- Playground: post-use CTA bar with copyable go get command (after first parse)
- Footer: add Release RSS feed link
- Blog: add missing v1.12.0–v1.13.0 comparison links to changelog footer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
ajitpratap0 pushed a commit that referenced this pull request Mar 22, 2026
… React error #310

useState(copied) and useCallback(handleCopy) were declared after the
if (loading) and if (error) early returns, violating the Rules of Hooks.

When the WASM binary was a 404, the component always hit the error branch
so both renders exited with the same hook count. After committing the WASM
binary (#423), the component now successfully transitions loading→ready,
causing React to see 11 hooks instead of 9 on the second render → #310.

Fix: move both hooks above all conditional returns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ajitpratap0 pushed a commit that referenced this pull request Mar 22, 2026
… React error #310

useState(copied) and useCallback(handleCopy) were declared after the
if (loading) and if (error) early returns, violating the Rules of Hooks.

When the WASM binary was a 404, the component always hit the error branch
so both renders exited with the same hook count. After committing the WASM
binary (#423), the component now successfully transitions loading→ready,
causing React to see 11 hooks instead of 9 on the second render → #310.

Fix: move both hooks above all conditional returns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ajitpratap0 added a commit that referenced this pull request Mar 22, 2026
… React error #310 (#429)

Hooks (useState for copied, useCallback for handleCopy) were placed after conditional early returns for loading/error states, violating React Rules of Hooks. Moved both hooks above the early returns so all 11 hooks are called unconditionally on every render.

Root cause: the hooks violation was previously masked because the WASM binary was missing (404), so the component always stayed in the error branch. Once the WASM binary was committed (#423), the loading→ready transition exposed the hook count mismatch, triggering React error #310.

Fixes #427
ajitpratap0 added a commit that referenced this pull request Mar 24, 2026
…, and CONNECT BY (#431)

* fix(playground): move useState/useCallback above early returns to fix React error #310

useState(copied) and useCallback(handleCopy) were declared after the
if (loading) and if (error) early returns, violating the Rules of Hooks.

When the WASM binary was a 404, the component always hit the error branch
so both renders exited with the same hook count. After committing the WASM
binary (#423), the component now successfully transitions loading→ready,
causing React to see 11 hooks instead of 9 on the second render → #310.

Fix: move both hooks above all conditional returns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(dialect): add DialectMariaDB constant to keyword dialect system

* fix(dialect): return MYSQL_SPECIFIC for DialectMariaDB and add to validDialects test

* fix(dialect): wire DialectMariaDB into keywords.New() to load MYSQL_SPECIFIC keywords

* test(dialect): add TestDialectMariaDB_InheritsMySQL to guard MySQL keyword inheritance

* feat(dialect): add MARIADB_SPECIFIC keyword list extending MySQL dialect

* feat(dialect): add MariaDB auto-detection hints (SEQUENCE, VERSIONING, CONNECT BY)

* fix(dialect): remove over-broad START WITH hint and complete DetectDialect doc comment

* fix(dialect): restore MariaDB CONNECT BY hint and add accumulation test

* feat(ast): add CreateSequenceStatement, DropSequenceStatement, AlterSequenceStatement nodes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ast): nil guard in sequence ToSQL, split Restart field, add sequence tests

- Guard s.Name nil dereference in CreateSequenceStatement.ToSQL,
  DropSequenceStatement.ToSQL, and AlterSequenceStatement.ToSQL
- Split SequenceOptions.Restart (*LiteralValue) into two fields:
  Restart bool (bare RESTART) and RestartWith *LiteralValue (RESTART WITH n)
- Update writeSequenceOptions to emit bare RESTART or RESTART WITH n accordingly
- Add ast_sequence_test.go with full ToSQL table-driven tests and pool round-trip test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ast): add ForSystemTimeClause, PeriodDefinition, temporal table fields

- Add SystemTimeClauseType enum with AS OF, BETWEEN, FROM/TO, ALL variants
- Add ForSystemTimeClause struct for MariaDB FOR SYSTEM_TIME temporal queries
- Add PeriodDefinition struct for PERIOD FOR clauses in CREATE TABLE
- Extend TableReference with ForSystemTime field (MariaDB 10.3.4+)
- Extend CreateTableStatement with WithSystemVersioning and PeriodDefinitions fields
- Add ForSystemTimeClause.ToSQL() and tableRefSQL integration in sql.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ast): add SQL() methods to temporal/CONNECT BY types and fix SelectStatement field order

Add SQL() methods to ForSystemTimeClause, ConnectByClause, and PeriodDefinition
(all implement expressionNode()) so they satisfy the Expression interface fully
without silently degrading via the exprSQL() fallback. Move StartWith and
ConnectBy fields in SelectStatement to directly follow Having, matching logical
SQL clause ordering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(parser): implement CREATE/DROP/ALTER SEQUENCE parsing for MariaDB dialect

Add parseCreateSequenceStatement, parseDropSequenceStatement, and
parseAlterSequenceStatement to mariadb.go with full option parsing
(START WITH, INCREMENT BY, MINVALUE, MAXVALUE, CYCLE, CACHE, RESTART WITH).
Wire dispatch into parseStatement() for DROP/ALTER and into parseCreateStatement()
for CREATE. Gate all paths behind isMariaDB() so MySQL and other dialects
are unaffected. Add six passing parser tests in mariadb_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(parser): implement temporal table parsing (FOR SYSTEM_TIME, WITH SYSTEM VERSIONING, PERIOD FOR)

Add parseForSystemTimeClause and parseTemporalPointExpression to mariadb.go
supporting AS OF, BETWEEN, FROM/TO, and ALL variants. Hook into
parseFromTableReference in select_subquery.go (after alias, before SQL Server
hints) with a peek-ahead guard so FOR is only consumed when followed by
SYSTEM_TIME. Add WITH SYSTEM VERSIONING parsing to parseCreateTable (after
closing paren, before PARTITION BY) and PERIOD FOR column parsing to the
column loop in ddl.go. Add four passing tests for temporal queries and
system versioning in mariadb_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(parser): implement CONNECT BY hierarchical query parsing for MariaDB dialect

Add parseConnectByCondition to mariadb.go which handles the PRIOR prefix
operator by wrapping the referenced column in a FunctionCall node and
building a BinaryExpression for the full PRIOR col = parent_col pattern.
Wire START WITH and CONNECT BY [NOCYCLE] parsing into parseSelectStatement
in select.go after the HAVING clause. Guard CONNECT and START from being
consumed as table aliases in parseFromTableReference via a peek-ahead check
in select_subquery.go. Add three passing tests covering basic, NOCYCLE, and
no-START-WITH variants in mariadb_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(parser): add bare RESTART test and nil guard for CONNECT BY condition

* test(parser): add MariaDB SQL test data files and file-based integration test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add MariaDB dialect to SQL_COMPATIBILITY.md and CHANGELOG.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mariadb): address code review issues across AST, keywords, and parser

AST layer:
- Add `Prior` UnaryOperator constant for CONNECT BY PRIOR expressions
- Add `NoCache bool` to SequenceOptions (NOCACHE was previously a silent no-op)
- Add `Pos models.Location` to all 6 new MariaDB AST nodes (CreateSequence,
  DropSequence, AlterSequence, ForSystemTimeClause, ConnectByClause, PeriodDefinition)
- Add `NewDropSequenceStatement()` / `ReleaseDropSequenceStatement()` pool funcs
  to match Create/Alter sequence pooling consistency
- Emit NOCACHE in writeSequenceOptions when NoCache=true

Keywords:
- Remove duplicate MINVALUE, MAXVALUE, INCREMENT, RESTART, NOCACHE entries
  from MARIADB_SPECIFIC (already covered by base/Oracle keyword lists)

Parser:
- Fix PRIOR operator: switch from FunctionCall to UnaryExpression{Operator: Prior}
- Fix PRIOR on RHS: CONNECT BY col = PRIOR col now parsed correctly
- Fix parseJoinedTableRef: add isMariaDBClauseKeyword guard to prevent CONNECT/START
  from being consumed as table aliases (same guard already in parseFromTableReference)
- Fix parseJoinedTableRef: add FOR SYSTEM_TIME temporal clause support on JOIN refs
- Fix DROP SEQUENCE: support IF NOT EXISTS in addition to IF EXISTS
- Fix DROP SEQUENCE: use NewDropSequenceStatement() for pool consistency
- Fix parseSequenceOptions: set opts.NoCache=true for NOCACHE keyword
- Add comment in parseTemporalPointExpression explaining quote-stripping behaviour

Tests:
- Add TestMariaDB_ConnectBy_PriorOnRight
- Add TestMariaDB_DropSequence_IfNotExists
- Add TestMariaDB_Sequence_NoCache
- Expand testdata SQL files with NO MINVALUE/MAXVALUE forms, PRIOR-on-right cases,
  IF NOT EXISTS on DROP, and multi-table temporal JOIN query

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mariadb): address second code review pass — Pos, NO CACHE, CONNECT BY, dedup

Parser dispatch (parser.go, ddl.go, select.go):
- Populate Pos on CreateSequenceStatement (at SEQUENCE token in ddl.go)
- Populate Pos on DropSequenceStatement (at DROP token in parser.go)
- Populate Pos on AlterSequenceStatement (at ALTER token in parser.go)
- Populate Pos on ConnectByClause (at CONNECT token in select.go)
- Populate Pos on PeriodDefinition (at PERIOD token in ddl.go)

mariadb.go:
- Fix NO CACHE (two-token) to also set opts.NoCache=true, matching NOCACHE
- Fix parseConnectByCondition to handle complex AND/OR chains:
  CONNECT BY PRIOR id = parent_id AND active = 1 now fully parsed
- Extract isMariaDBClauseStart() method (was duplicated closure in two functions)
- Populate Pos on ForSystemTimeClause (at SYSTEM_TIME token)
- Add comment clarifying IF NOT EXISTS is a non-standard permissive extension

select_subquery.go:
- Remove both isMariaDBClauseKeyword closures, replace with p.isMariaDBClauseStart()

ast.go:
- Update DropSequenceStatement doc to show [IF EXISTS | IF NOT EXISTS]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mariadb): correct START WITH/CONNECT BY SQL order and implement PeriodDefinition.SQL()

- Move START WITH / CONNECT BY emission to after HAVING, before ORDER BY
- Implement PeriodDefinition.SQL() — was silently returning empty string
- Add round-trip tests for both fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mariadb): code review fixes — pool comments, expressionNode docs, PERIOD FOR SYSTEM_TIME test

- Add '// zero all fields' comment to ReleaseCreateSequenceStatement and ReleaseAlterSequenceStatement
- Add design-choice comments on expressionNode() for ForSystemTimeClause, ConnectByClause, PeriodDefinition
- Add TestMariaDB_CreateTable_PeriodForSystemTime to cover PERIOD FOR SYSTEM_TIME parsing
- Fix parsePeriodDefinition to use parseColumnName so SYSTEM_TIME (reserved keyword) is accepted as period name
- Add GENERATED ALWAYS AS ROW START/END column constraint parsing in parseColumnConstraint (MariaDB system-versioned columns)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(dialect): reduce MariaDB CONNECT BY hint weight to 2 so Oracle wins pure CONNECT BY queries

Oracle's CONNECT BY weight is 3; MariaDB's was also 3, causing a tie broken
by non-deterministic map iteration. Reducing MariaDB to weight 2 ensures
Oracle wins when CONNECT BY is the only hint. MariaDB is still correctly
detected when its unique high-weight hints (NEXTVAL, FOR SYSTEM_TIME, etc.)
are present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mariadb): address code review — CycleOption enum, CACHE validation, structured errors

- Replace Cycle bool + NoCycle bool with CycleOption enum to prevent
  invalid state where both could be true simultaneously
- Add validation in parseSequenceOptions for contradictory CACHE/NOCACHE
- Replace fmt.Errorf with p.expectedError() for consistent error style

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* bench(mariadb): add MariaDB-specific parsing benchmarks

17 benchmarks across 4 groups:
- BenchmarkMariaDB_Sequence: CREATE/ALTER/DROP SEQUENCE (5 cases)
- BenchmarkMariaDB_ForSystemTime: temporal table queries (5 cases)
- BenchmarkMariaDB_ConnectBy: hierarchical queries with PRIOR (4 cases)
- BenchmarkMariaDB_Mixed: combined features — CTE+temporal, CTE+CONNECT BY,
  CREATE TABLE WITH SYSTEM VERSIONING (3 cases)

Baseline (M2): 398–3001 ns/op, 984–6763 B/op

---------

Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant