Skip to content

Commit bc2b91c

Browse files
joaoh82claude
andauthored
Rename npm package to @joaoh82/sqlrite (scoped) (#30)
The unscoped `sqlrite` name on npm was rejected during the v0.1.5 canary setup with: 403 Forbidden — Package name too similar to existing packages sqlite, sqlite3; try renaming your package to '@joaoh82/sqlrite' and publishing with 'npm publish --access=public' instead. Levenshtein distance between "sqlrite" and "sqlite" is 1, which trips npm's similar-names check. This is a registry-side policy, not something we can work around with a suffix — `sqlrite-db`, `sqlrite-node`, etc. all share the same stem and would hit the same check. **Switch to `@joaoh82/sqlrite`** — npm's own recommended fix, and the standard pattern in the Node ecosystem (`@napi-rs/canvas`, `@aws-sdk/client-s3`, `@swc/core`, `@types/*`). Scoped names bypass the similarity check because the scope itself is a namespace. The scope `@joaoh82` is auto-owned by the author's npm user account, so no additional reservation step needed. **Paralleling the crates.io workaround:** this is the same registry-forced rename pattern we hit with `sqlrite-engine` on crates.io (the `sqlrite` name there was taken by an unrelated RAG-SQLite wrapper). Different registries, different reasons, same pragmatic outcome — rename the *package* while keeping the Rust import alias stable inside the codebase. Files touched (12): Source & manifests: sdk/nodejs/package.json — name: "@joaoh82/sqlrite" sdk/nodejs/package-lock.json — refreshed via npm install sdk/nodejs/Cargo.toml — description + comment sdk/nodejs/src/lib.rs — module doc-comment Workflows: .github/workflows/release.yml — npm URL + install/require snippets in release body, comment block explaining --access public requirement Docs: README.md — Phase 6c runbook summary docs/embedding.md — distribution channel list docs/release-plan.md — publish-channels table docs/release-secrets.md — §3 npm trusted-publisher flow, now notes scoped names auto-own + skip bootstrap NPM_TOKEN dance docs/roadmap.md — Phase 6c trusted-publishers bullet examples/README.md — language→publish-channel table sdk/nodejs/README.md gets a new "Why the scoped name?" callout near the top explaining the rename to users reading the readme cold. No engine changes, no src/ changes outside the doc-comment in sdk/nodejs/src/lib.rs. The underlying napi-rs binding is byte- identical; only how npm addresses the tarball changed. Next release canary (v0.1.5) will push the first real build under the new name. First publish also auto-creates the scoped package, so the trusted-publisher registration on npmjs.com can run straight against the non-existent-yet package name (npm's chicken-and-egg from the PyPI comparison doesn't apply to scoped names under your own user scope). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 03a4972 commit bc2b91c

12 files changed

Lines changed: 65 additions & 55 deletions

File tree

.github/workflows/release.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -811,9 +811,11 @@ jobs:
811811
812812
# Single atomic publish. `--provenance` signs the package
813813
# with a sigstore-backed attestation linking it to this
814-
# workflow run. `--access public` because scoped packages
815-
# default to private — we're using an unscoped name, but
816-
# the flag is harmless and future-proofs against a rename.
814+
# workflow run. `--access public` is REQUIRED because
815+
# `@joaoh82/sqlrite` is a scoped package and scoped
816+
# packages default to private; without the flag, npm
817+
# would reject the upload for a free-tier account that
818+
# can't host private packages.
817819
- name: Publish to npm
818820
working-directory: sdk/nodejs
819821
run: npm publish --provenance --access public
@@ -827,14 +829,14 @@ jobs:
827829
tag_name: sqlrite-node-v${{ needs.detect.outputs.version }}
828830
name: Node.js v${{ needs.detect.outputs.version }}
829831
body: |
830-
Published to npm: https://www.npmjs.com/package/sqlrite/v/${{ needs.detect.outputs.version }}
832+
Published to npm: https://www.npmjs.com/package/@joaoh82/sqlrite/v/${{ needs.detect.outputs.version }}
831833
832834
```bash
833-
npm install sqlrite@${{ needs.detect.outputs.version }}
835+
npm install @joaoh82/sqlrite@${{ needs.detect.outputs.version }}
834836
```
835837
836838
```javascript
837-
const { Database } = require('sqlrite');
839+
const { Database } = require('@joaoh82/sqlrite');
838840
839841
const db = new Database(':memory:');
840842
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
@@ -892,7 +894,7 @@ jobs:
892894
- 🔧 [C FFI](../../releases/tag/sqlrite-ffi-v${{ needs.detect.outputs.version }}) — prebuilt `libsqlrite_c` for Linux x86_64/aarch64, macOS aarch64, Windows x86_64
893895
- 🖥️ [Desktop](../../releases/tag/sqlrite-desktop-v${{ needs.detect.outputs.version }}) — unsigned installers for Linux (AppImage + deb), macOS (dmg aarch64), Windows (msi)
894896
- 🐍 [Python](../../releases/tag/sqlrite-py-v${{ needs.detect.outputs.version }}) → [PyPI](https://pypi.org/project/sqlrite/${{ needs.detect.outputs.version }}/) — abi3-py38 wheels for Linux x86_64/aarch64, macOS aarch64, Windows x86_64 + sdist
895-
- 🟢 [Node.js](../../releases/tag/sqlrite-node-v${{ needs.detect.outputs.version }}) → [npm](https://www.npmjs.com/package/sqlrite/v/${{ needs.detect.outputs.version }}) — N-API bindings with prebuilt `.node` binaries for Linux x86_64/aarch64, macOS aarch64, Windows x86_64
897+
- 🟢 [Node.js](../../releases/tag/sqlrite-node-v${{ needs.detect.outputs.version }}) → [npm](https://www.npmjs.com/package/@joaoh82/sqlrite/v/${{ needs.detect.outputs.version }}) — N-API bindings with prebuilt `.node` binaries for Linux x86_64/aarch64, macOS aarch64, Windows x86_64
896898
897899
_WASM / Go SDKs land as their publish jobs come online (Phases 6h–6i)._
898900

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ Lockstep versioning — one dispatch bumps every product to the same `vX.Y.Z`. T
224224

225225
- [x] **6a — Bump script**: `scripts/bump-version.sh` rewrites the version string in ten manifests (7 TOML, 3 JSON) in a single pass; semver-validated, idempotent, cross-platform (BSD + GNU sed). Runnable locally for rehearsing a release: `./scripts/bump-version.sh 0.2.0 && cargo build && git diff`.
226226
- [x] **6b — CI**: `.github/workflows/ci.yml` runs on every PR + push to main. Seven parallel jobs: `rust-build-and-test` (Linux/macOS/Windows × cargo build + test), `rust-lint` (fmt + clippy + doc), `python-sdk` (Linux/macOS/Windows × maturin develop + pytest in a venv), `nodejs-sdk` (Linux/macOS/Windows × napi build + node --test), `go-sdk` (Linux/macOS × cargo build sqlrite-ffi + go test), `wasm-build` (wasm-pack + size report), `desktop-build` (npm ci + Tauri Rust compile). Cargo / npm / pip caching for fast PR turnaround.
227-
- [x] **6c — Trusted publisher setup + branch protection runbook**: [`docs/release-secrets.md`](docs/release-secrets.md) captures the one-time web-UI setup — crates.io token in the `release` environment, OIDC trusted publishers on PyPI (`sqlrite`) and npm (`sqlrite` + `sqlrite-wasm`), GitHub `release` environment with required reviewer, branch protection on `main` requiring 14 CI jobs + 1 review. No code changes — executable as-is, ready to run through in the GitHub + registry UIs.
227+
- [x] **6c — Trusted publisher setup + branch protection runbook**: [`docs/release-secrets.md`](docs/release-secrets.md) captures the one-time web-UI setup — crates.io token in the `release` environment, OIDC trusted publishers on PyPI (`sqlrite`) and npm (`@joaoh82/sqlrite` + `sqlrite-wasm` — Node binding is scoped because npm's similarity check rejects the unscoped name against `sqlite`/`sqlite3`), GitHub `release` environment with required reviewer, branch protection on `main` requiring 14 CI jobs + 1 review. No code changes — executable as-is, ready to run through in the GitHub + registry UIs.
228228
- [x] **6d — Release PR + skeleton publish**: two workflows under `.github/workflows/`. `release-pr.yml` (manual dispatch with version input → bump-version.sh → PR), `release.yml` (fires on `release: v<semver>` merge commit → `tag-all` + `publish-crate` + `publish-ffi` matrix [linux x86_64/aarch64, macOS aarch64, windows x86_64] + umbrella release). Idempotent tag creation so "Re-run failed jobs" works after partial failures. `cargo publish` gated by the `release` environment's required-reviewer rule. First canary: `v0.1.1`.
229229
- [ ] **6e — Desktop publish**: add `publish-desktop` to `release.yml` — Tauri build matrix → unsigned `.AppImage` / `.deb` / `.dmg` / `.msi` → GitHub Release
230230
- [ ] **6f — Python SDK publish**: `maturin-action` → abi3 wheels for manylinux x86_64/aarch64 + macOS universal + Windows x86_64 → PyPI via OIDC

docs/embedding.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ Phase 6 lands GitHub Actions CI + release automation:
217217

218218
- **crates.io**`sqlrite-engine` crate (published under a different name from the `sqlrite` lib target because the short name was already taken; users `cargo add sqlrite-engine` but still write `use sqlrite::…`)
219219
- **PyPI**`sqlrite` wheels (manylinux x86_64/aarch64, macOS universal, Windows x86_64)
220-
- **npm**`sqlrite` (Node) + `sqlrite-wasm` (browser) packages
220+
- **npm**`@joaoh82/sqlrite` (Node) + `sqlrite-wasm` (browser) packages
221221
- **Go modules**`sdk/go/v*` git tags
222222
- **GitHub Releases** — Tauri desktop builds + C FFI prebuilt libraries
223223

docs/release-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ GitHub Releases by product ("show me every Python release").
7373
| Rust engine | `sqlrite-vX.Y.Z` | crates.io + GitHub Release |
7474
| C FFI shim | `sqlrite-ffi-vX.Y.Z` | GitHub Release (per-platform tarballs) |
7575
| Python SDK | `sqlrite-py-vX.Y.Z` | PyPI + GitHub Release |
76-
| Node.js SDK | `sqlrite-node-vX.Y.Z` | npm + GitHub Release |
76+
| Node.js SDK | `sqlrite-node-vX.Y.Z` | npm (`@joaoh82/sqlrite`) + GitHub Release |
7777
| Go SDK | `sdk/go/vX.Y.Z` | Git tag (no registry) + GitHub Release assets |
7878
| WASM | `sqlrite-wasm-vX.Y.Z` | npm (`sqlrite-wasm`) + GitHub Release |
7979
| Desktop app | `sqlrite-desktop-vX.Y.Z` | GitHub Release (unsigned installers) |

docs/release-secrets.md

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -105,48 +105,51 @@ release, status flips to "active".
105105

106106
## 3. npm trusted publishers (two packages)
107107

108-
**Why two:** we publish `sqlrite` (Node.js bindings from
108+
**Why two:** we publish `@joaoh82/sqlrite` (Node.js bindings from
109109
`sdk/nodejs/`) and `sqlrite-wasm` (browser bindings from
110110
`sdk/wasm/`) as separate npm packages. Each needs its own
111111
trusted-publisher record.
112112

113-
### 3a. Reserve `sqlrite` on npm
113+
**Why the scoped name on the Node.js package:** npm's registry
114+
rejects the unscoped `sqlrite` name because of a similarity check
115+
against the existing `sqlite` / `sqlite3` packages (levenshtein
116+
distance 1). Scoping under `@joaoh82` (the author's npm user
117+
scope) bypasses the check cleanly — same pattern as `@napi-rs/*`,
118+
`@swc/core`, etc. Discovered during the v0.1.5 canary attempt;
119+
rename PR landed before the retry. Applies to the Node.js
120+
package only; `sqlrite-wasm` may or may not hit the same check
121+
when 6h lands (if it does, rename to `@joaoh82/sqlrite-wasm`).
114122

115-
1. Log in at <https://www.npmjs.com/>.
116-
2. Search for `sqlrite` — confirm it's not taken. (If it is,
117-
we'll rename — update `sdk/nodejs/package.json`'s `name` +
118-
`napi.name` to something like `@joaoh82/sqlrite` and file an
119-
issue.)
123+
### 3a. Reserve `@joaoh82/sqlrite` on npm
120124

121-
### 3b. Trusted publisher for `sqlrite`
125+
1. Log in at <https://www.npmjs.com/> as `joaoh82`.
126+
2. Scoped packages under your user scope are auto-owned — no
127+
manual name reservation step required. First-time publish
128+
against the trusted publisher will create the package.
129+
130+
### 3b. Trusted publisher for `@joaoh82/sqlrite`
122131

123132
npm's trusted publishing flow:
124133

125-
1. From the package page (after first manual publish, or via
126-
<https://www.npmjs.com/package/sqlrite> once pre-registered),
127-
click **Settings****Trusted Publishers**.
134+
1. Go to <https://www.npmjs.com/settings/~/packages> (or your
135+
profile → Packages) → **Trusted Publishers** tab.
128136
2. **Add a new publisher**:
137+
- **Package name**: `@joaoh82/sqlrite`
129138
- **Publisher**: GitHub Actions
130139
- **Organization or user**: `joaoh82`
131140
- **Repository**: `rust_sqlite`
132141
- **Workflow filename**: `release.yml`
133142
- **Environment**: `release`
134143
3. Save.
135144

136-
**npm's chicken-and-egg:** npm doesn't offer a "pending
137-
publisher" mode like PyPI. A package must exist before you can
138-
add a trusted publisher for it. First-time publish workaround:
139-
140-
- Use a classic npm access token (generate one scoped to publish
141-
for `sqlrite` only) for the very first publish only.
142-
- Store it temporarily as `NPM_TOKEN` in the `release`
143-
environment's secrets.
144-
- After the first publish succeeds, add the trusted publisher
145-
via the UI, then delete `NPM_TOKEN` from the secrets.
146-
- On the next release, OIDC takes over automatically.
147-
148-
Document the temporary token in a pinned issue so it gets
149-
rotated out once OIDC is live.
145+
**npm's chicken-and-egg, resolved for scoped packages:** For
146+
unscoped names (like the abandoned `sqlrite` attempt), npm
147+
requires the package to already exist before you can add a
148+
trusted publisher — first publish needs a temporary `NPM_TOKEN`.
149+
For scoped packages under your own user scope, npm auto-owns
150+
the scope, and the trusted-publisher registration works against
151+
the future package before it exists. First CI publish creates
152+
it cleanly with OIDC. **No temporary token needed.**
150153

151154
### 3c. Repeat for `sqlrite-wasm`
152155

docs/roadmap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ One-time non-code setup — the state lives in registry web UIs + GitHub setting
360360

361361
1. **crates.io API token**`CRATES_IO_TOKEN` in the `release` environment's secrets (crates.io doesn't support OIDC yet, so this is the only long-lived token in the pipeline).
362362
2. **PyPI trusted publisher** pointed at `release.yml` / environment `release` — short-lived OIDC tokens, no secret to leak.
363-
3. **npm trusted publishers** for both `sqlrite` (the Node binding) and `sqlrite-wasm` (the browser binding). npm doesn't have a pending-publisher mode like PyPI, so the runbook captures the first-release bootstrap: temporary `NPM_TOKEN`, then swap to OIDC.
363+
3. **npm trusted publishers** for both `@joaoh82/sqlrite` (the Node binding — scoped because npm rejected the unscoped `sqlrite` name as too similar to `sqlite`/`sqlite3`) and `sqlrite-wasm` (the browser binding). Scoped packages under your own user scope auto-own the name, so the trusted-publisher flow works without a bootstrap `NPM_TOKEN`. See `docs/release-secrets.md` §3 for the full flow.
364364
4. **GitHub `release` environment** — required reviewer (maintainer), `main`-only deployments, scoped secrets. Acts as a second human-in-the-loop gate after the Release PR merge but before any registry write.
365365
5. **Branch protection on `main`** — require 14 CI status checks green + 1 review + conversation resolution. Admin bypass left available for emergencies.
366366

examples/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ Phase 5 lands these incrementally — each sub-phase fills in one language. The
66

77
| Language | Status | SDK published | Directory |
88
|----------|--------|---------------|-----------|
9-
| Rust | ✅ Phase 5a | crates.io (Phase 6c) | [`rust/`](rust/) |
9+
| Rust | ✅ Phase 5a | crates.io as `sqlrite-engine` (Phase 6d) | [`rust/`](rust/) |
1010
| C (FFI) | ✅ Phase 5b | GitHub Releases (Phase 6d) | [`c/`](c/) |
11-
| Python | ✅ Phase 5c | PyPI (Phase 6e) | [`python/`](python/) |
12-
| Node.js | ✅ Phase 5d | npm (Phase 6e) | [`nodejs/`](nodejs/) |
13-
| Go | ✅ Phase 5e | Go modules (Phase 6e)| [`go/`](go/) |
14-
| WASM | ✅ Phase 5g | npm as `sqlrite-wasm` (Phase 6e) | [`wasm/`](wasm/) |
11+
| Python | ✅ Phase 5c | PyPI as `sqlrite` (Phase 6f) | [`python/`](python/) |
12+
| Node.js | ✅ Phase 5d | npm as `@joaoh82/sqlrite` (Phase 6g) | [`nodejs/`](nodejs/) |
13+
| Go | ✅ Phase 5e | Go modules (Phase 6i) | [`go/`](go/) |
14+
| WASM | ✅ Phase 5g | npm as `sqlrite-wasm` (Phase 6h) | [`wasm/`](wasm/) |
1515

1616
See [docs/roadmap.md](../docs/roadmap.md) for what each sub-phase delivers.
1717

sdk/nodejs/Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.4"
44
authors = ["Joao Henrique Machado Silva <joaoh82@gmail.com>"]
55
edition = "2024"
66
rust-version = "1.85"
7-
description = "Node.js bindings for the SQLRite embedded database engine. Ships the `sqlrite` npm package with prebuilt `.node` binaries."
7+
description = "Node.js bindings for the SQLRite embedded database engine. Ships the `@joaoh82/sqlrite` npm package with prebuilt `.node` binaries."
88
repository = "https://github.com/joaoh82/rust_sqlite"
99
license = "MIT"
1010

@@ -14,8 +14,10 @@ license = "MIT"
1414
#
1515
# Named `sqlrite_nodejs` so the rlib doesn't collide with the root
1616
# `sqlrite` crate or with the FFI crate's `sqlrite_c`. The user-
17-
# facing import is `const { Database } = require('sqlrite')` — that
18-
# naming lives in package.json, not Cargo.
17+
# facing import is `const { Database } = require('@joaoh82/sqlrite')`
18+
# — scoped because the unscoped `sqlrite` name was rejected by npm
19+
# as too similar to `sqlite`/`sqlite3`. That naming lives in
20+
# package.json, not Cargo.
1921
[lib]
2022
name = "sqlrite_nodejs"
2123
crate-type = ["cdylib", "rlib"]

sdk/nodejs/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
# sqlrite (Node.js)
1+
# @joaoh82/sqlrite (Node.js)
22

33
Node.js bindings for [SQLRite](https://github.com/joaoh82/rust_sqlite) — a small, embeddable SQLite clone written in Rust. Shape follows [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3) (sync API, row-as-object), so Node developers who've used that library can pick this up without reading the docs.
44

5+
> **Why the scoped name?** npm's registry rejected the unscoped `sqlrite` name as too similar to the existing `sqlite` / `sqlite3` packages. Scoping under `@joaoh82` (my npm user scope) bypasses that check cleanly — same pattern as `@napi-rs/canvas`, `@swc/core`, `@aws-sdk/client-s3`.
6+
57
## Install
68

79
```bash
8-
# From npm (once Phase 6e's CI/CD release pipeline is live):
9-
npm install sqlrite
10+
npm install @joaoh82/sqlrite
1011

1112
# From source in a clone of the repo:
1213
cd sdk/nodejs
@@ -17,7 +18,7 @@ npm run build # produces sqlrite.<platform>-<arch>.node
1718
## Quick tour
1819

1920
```js
20-
import { Database } from 'sqlrite';
21+
import { Database } from '@joaoh82/sqlrite';
2122

2223
// File-backed or in-memory (use `":memory:"` to match better-sqlite3).
2324
const db = new Database('foo.sqlrite');

sdk/nodejs/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)