Status: Reference / runbook Last updated: 2026-06-03
- npm organization:
sqlanvil(free tier, public packages) - Placeholders published 2026-05-27:
@sqlanvil/cli@0.0.1,@sqlanvil/core@0.0.1(name reservations only) - Versioning is sqlanvil's own SemVer line:
SQLANVIL_VERSIONinversion.bzl(currently 1.0.0). The Bazel build stamps the npm packages from it.DF_VERSION(currently 3.0.59) is the upstream dataform release this fork is synced to — kept as metadata (surfaced insqlanvil --versionassqlanvil 1.0.0 (Dataform core 3.0.59)and exported asdataformVersionfrom@sqlanvil/core), not the package version. BumpSQLANVIL_VERSIONfor sqlanvil releases; bumpDF_VERSIONon upstream syncs. See §4. - Real publish target: Bazel-built tarball at
bazel-bin/packages/@sqlanvil/cli/package.tar.gz(and matching forcore) - For testing, don't publish — install the built tarball locally
(
npm i ./sqlanvil-cli.tgz). Only publish to npm when you specifically need the install-from-registry path (the statelesssqlanvilCoreVersionflow), and then publish a-beta.Nprerelease under--tag betaso you never burn a real version number. See §3.0.
- Name:
sqlanvil - Owner: Ivan Histand (
ihistandon npm) - Plan: Free (unlimited public packages, no private packages)
- Created: 2026-05-27
- URL: https://www.npmjs.com/org/sqlanvil
Free plan is sufficient — sqlanvil is OSS, no private packages needed.
| Package | Version | Date | Status |
|---|---|---|---|
@sqlanvil/cli |
0.0.1 |
2026-05-27 | placeholder (name reservation) |
@sqlanvil/core |
0.0.1 |
2026-05-27 | placeholder (name reservation) |
Both placeholders print "not yet published — see GitHub" and exit
non-zero. Source not committed — one-shot scaffolding under
~/sqlanvil-npm-placeholders/ on Ivan's Mac.
Note: The next real publish will be
1.0.0(SQLANVIL_VERSION), not0.0.2— sqlanvil's own SemVer line, decoupled from the Dataform base. See §4. The0.0.1placeholders are superseded the first time a real1.xbuild is published.
Don't grab speculatively (npm anti-squatting policy may reclaim dormant placeholders after 6+ months). Claim when implementation is within 1–2 phases of needing them:
| Package | Probable timing | Purpose |
|---|---|---|
@sqlanvil/postgres |
Phase 3b–4 | If Postgres adapter ships standalone |
@sqlanvil/supabase |
Phase 5 | Supabase variant adapter |
@sqlanvil/protos |
Phase 4 | Generated TS proto types as standalone module |
sqlanvil (unscoped) |
optional | Vanity name — easier npm i sqlanvil |
For almost all testing you do not need npm. Build the tarball and install it locally — it's the identical artifact you'd publish, with zero registry impact:
cd ~/projects-ivan/sqlanvil
./scripts/docker-bazel build //packages/@sqlanvil/cli:package_tar \
//packages/@sqlanvil/core:package_tar \
--jobs=2 --local_ram_resources=2048
# Extract from the Docker bazel volume to the host (see §3.2), then in a
# throwaway test project:
npm i /path/to/sqlanvil-core.tgz
npm i -g /path/to/sqlanvil-cli.tgzThe one thing a local install can't exercise: the stateless install
path, where cli/api/commands/compile.ts runs npm i @sqlanvil/core@<ver>
from the registry (triggered by sqlanvilCoreVersion: in
workflow_settings.yaml). To validate that flow end-to-end the package must
be on npm — publish a beta (§3.4), never the real version, for testing.
Native macOS Bazel is currently broken on this host (the wrapped_clang
toolchain hits a dyld LC_UUID error when compiling protobuf's C++), so all
builds go through scripts/docker-bazel (Linux toolchain):
cd ~/projects-ivan/sqlanvil
./scripts/docker-bazel build //packages/@sqlanvil/cli:package_tar \
--jobs=2 --local_ram_resources=2048
# Output: bazel-bin/packages/@sqlanvil/cli/package.tar.gz--jobs=2 --local_ram_resources=2048 avoids OOM-killing the in-container
Bazel JVM during the webpack bundling step.
The tarball is platform-agnostic — pure JS bundle, no native deps in the
tarball itself. Native modules like pg are listed in package.json and
rebuilt by npm on the consumer's machine.
bazel-bin/ symlinks into the Docker named volume, so you can't cp from
the host. Run the copy inside a container with the workspace mounted:
docker run --rm \
-v "$PWD:/workspace" \
-v sqlanvil-bazel-cache:/root/.cache/bazel \
-v sqlanvil-bazel-disk:/root/.cache/bazel-disk \
sqlanvil-dev \
cp /workspace/bazel-bin/packages/@sqlanvil/cli/package.tar.gz \
/workspace/sqlanvil-cli.tgzTarball now at ./sqlanvil-cli.tgz (host filesystem). Repeat for core.
tar tzf sqlanvil-cli.tgz | head -20
# Should show: package/package.json, package/bundle.js, etc.
tar xzf sqlanvil-cli.tgz -C /tmp/inspect && cat /tmp/inspect/package/package.json
# Verify name + version are what you expect (version == SQLANVIL_VERSION, e.g. 1.0.0)npm login # 2FA device-auth flow (see §6)
# Real release (version == SQLANVIL_VERSION, e.g. 1.0.0):
npm publish ./sqlanvil-cli.tgz --access public
# Beta / test release (set SQLANVIL_VERSION = "1.0.0-beta.0" first, rebuild):
npm publish ./sqlanvil-cli.tgz --access public --tag beta--tag beta keeps the prerelease off the latest dist-tag, so
npm i @sqlanvil/cli users never receive it; testers opt in with
npm i @sqlanvil/cli@beta. Publish core first, then cli.
sqlanvil has its own SemVer line, SQLANVIL_VERSION (in version.bzl,
currently 1.0.0) — this is the published @sqlanvil/* version. DF_VERSION
(currently 3.0.59) is the upstream dataform-co/dataform release the fork is
synced to, kept as metadata only: surfaced in sqlanvil --version
(sqlanvil 1.0.0 (Dataform core 3.0.59)) and exported as dataformVersion from
@sqlanvil/core. The two are independent (you can't express "our build N on top
of 3.0.59" in 3-segment SemVer like 3.0.59.1, so the package version is decoupled).
Why decouple (supersedes the earlier "track DF_VERSION" policy): SemVer is three
segments, so pinning the package to the exact upstream patch leaves no room for
sqlanvil's own iterative releases between upstream syncs. @sqlanvil/core is a
different npm package than @dataform/core, so there is no version collision to
avoid — mirroring DF_VERSION only ever helped human signaling, which the
--version / dataformVersion metadata now provides.
The CLI's compile-time cli/core compatibility check derives its floor from the
CLI's own version (cli/vm/compile.ts requires a matching major + core ≥
cli.major.minor.0), so cli@X + core@X (both stamped from SQLANVIL_VERSION)
stay in lockstep automatically. Dataform-capability gates inside the SQL
generators (e.g. incremental pre/post-ops at > 1.4.8, the pre-3.0.57 caller-file
shim) compare against the Dataform version (dataformVersion), not the package
version — so decoupling does not disable them.
- sqlanvil release: bump
SQLANVIL_VERSIONinversion.bzl(e.g.1.0.0→1.0.1/1.1.0). - Upstream sync: bump
DF_VERSION(thescripts/update_versionhelper) — this changes the displayed Dataform base, not the package version. - Prerelease / test:
SQLANVIL_VERSION = "X.Y.Z-beta.N", published--tag beta. Never reuse a real version number — npm versions are permanent and immutable. - Don't publish at all for local testing — use §3.0.
Once GitHub Actions is wired, publish becomes a tag-triggered CI step:
# .github/workflows/release.yml
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/docker-bazel build //packages/@sqlanvil/cli:package_tar
- run: docker cp $(docker create sqlanvil-dev):/workspace/bazel-bin/packages/@sqlanvil/cli/package.tar.gz ./pkg.tgz
- uses: actions/setup-node@v4
with:
registry-url: https://registry.npmjs.org
- run: npm publish ./pkg.tgz --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}Token setup: npm → Profile → Access Tokens → Generate (Granular, scoped to
@sqlanvil/*) → store as NPM_TOKEN GitHub secret.
- 2FA is enabled by default for new npm accounts. Each
npm publishfrom a CLI triggers a browser device-auth flow. - For automation, use granular access tokens (npm Profile → Access Tokens
→ "Granular") scoped only to
@sqlanvil/*packages. Less blast radius than classic legacy tokens.
- npm scopes docs
- npm publishing policy
- Anti-squatting policy
upstream_merge_guide.md— how upstream syncs land onmainpostgres_first_class_design.md§9 — phase plan