From 80bc68ed0539026427726985bbed284337e1967e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Apr 2026 14:39:47 +0000 Subject: [PATCH 1/3] Add platform-specific npm packages and publish workflow Set up the esbuild-style pattern for distributing prebuilt binaries: - Add platform packages under npm/ for linux-x64, darwin-arm64, darwin-x64, and win32-x64 (each with os/cpu fields so npm only installs the matching one) - Update main package.json with optionalDependencies on all four - Rewrite nodejs-helper.js/cjs to resolve from platform packages first, falling back to local build/ then dist/ for development - Add npm-publish.yaml workflow: builds on all 4 platforms, runs integration tests, then publishes all packages on GitHub release To publish: create a GitHub release (tag matching the version), with NPM_TOKEN set as a repository secret. https://claude.ai/code/session_0156tM6LmjAC3xYz5xwETvym --- .github/workflows/npm-publish.yaml | 121 +++++++++++++++++++++++++ core/nodejs-helper.cjs | 35 ++++++- core/nodejs-helper.js | 45 ++++++++- core/package.json | 9 +- npm/crsqlite-darwin-arm64/index.js | 2 + npm/crsqlite-darwin-arm64/package.json | 15 +++ npm/crsqlite-darwin-x64/index.js | 2 + npm/crsqlite-darwin-x64/package.json | 15 +++ npm/crsqlite-linux-x64/index.js | 2 + npm/crsqlite-linux-x64/package.json | 15 +++ npm/crsqlite-win32-x64/index.js | 2 + npm/crsqlite-win32-x64/package.json | 15 +++ 12 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/npm-publish.yaml create mode 100644 npm/crsqlite-darwin-arm64/index.js create mode 100644 npm/crsqlite-darwin-arm64/package.json create mode 100644 npm/crsqlite-darwin-x64/index.js create mode 100644 npm/crsqlite-darwin-x64/package.json create mode 100644 npm/crsqlite-linux-x64/index.js create mode 100644 npm/crsqlite-linux-x64/package.json create mode 100644 npm/crsqlite-win32-x64/index.js create mode 100644 npm/crsqlite-win32-x64/package.json diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml new file mode 100644 index 000000000..5709066ed --- /dev/null +++ b/.github/workflows/npm-publish.yaml @@ -0,0 +1,121 @@ +on: + release: + types: [published] +name: "npm-publish" + +# The release tag must match the version in package.json (e.g. v0.1.0). +# Set NPM_TOKEN as a repository secret to authenticate with npm. + +jobs: + build: + name: Build ${{ matrix.platform }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + platform: linux-x64 + ext: so + - os: macos-14 + platform: darwin-arm64 + ext: dylib + - os: macos-13 + platform: darwin-x64 + ext: dylib + - os: windows-latest + platform: win32-x64 + ext: dll + steps: + - uses: actions/checkout@v4 + + - name: Build extension + run: | + cd core + cmake -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build --config Release + + - name: Copy binary to platform package + shell: bash + run: | + cp core/build/crsqlite.${{ matrix.ext }} npm/crsqlite-${{ matrix.platform }}/crsqlite.${{ matrix.ext }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: crsqlite-${{ matrix.platform }} + path: npm/crsqlite-${{ matrix.platform }}/ + + test: + name: Test ${{ matrix.os }} + needs: build + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + platform: linux-x64 + - os: macos-14 + platform: darwin-arm64 + - os: macos-13 + platform: darwin-x64 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Build extension + run: | + cd core + cmake -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build + + - name: Run integration tests + run: | + cd core + node --test test/integration.test.mjs + + publish: + name: Publish to npm + needs: [build, test] + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "22" + registry-url: "https://registry.npmjs.org" + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts/ + + - name: Prepare platform packages + run: | + for platform in linux-x64 darwin-arm64 darwin-x64 win32-x64; do + cp -r artifacts/crsqlite-${platform}/* npm/crsqlite-${platform}/ + echo "Contents of npm/crsqlite-${platform}:" + ls -la npm/crsqlite-${platform}/ + done + + - name: Publish platform packages + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + for platform in linux-x64 darwin-arm64 darwin-x64 win32-x64; do + cd npm/crsqlite-${platform} + npm publish --access public || echo "Skipped crsqlite-${platform} (may already exist)" + cd ../.. + done + + - name: Publish main package + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + cd core + npm publish --access public diff --git a/core/nodejs-helper.cjs b/core/nodejs-helper.cjs index 427cc8b4c..4c8c0e7e5 100644 --- a/core/nodejs-helper.cjs +++ b/core/nodejs-helper.cjs @@ -1,4 +1,33 @@ -// CJS entry point — see nodejs-helper.js for usage examples. +// CJS entry point — see nodejs-helper.js for usage and resolution order. const { join } = require("node:path"); -const extensionPath = join(__dirname, "dist", "crsqlite"); -module.exports = { extensionPath }; +const { existsSync } = require("node:fs"); + +const PLATFORM_PACKAGES = { + "linux-x64": "@anthropic/crsqlite-linux-x64", + "darwin-arm64": "@anthropic/crsqlite-darwin-arm64", + "darwin-x64": "@anthropic/crsqlite-darwin-x64", + "win32-x64": "@anthropic/crsqlite-win32-x64", +}; + +function resolve() { + const key = `${process.platform}-${process.arch}`; + const pkg = PLATFORM_PACKAGES[key]; + if (pkg) { + try { + return require(pkg).path; + } catch {} + } + + const buildPath = join(__dirname, "build", "crsqlite"); + if ( + existsSync(buildPath + ".so") || + existsSync(buildPath + ".dylib") || + existsSync(buildPath + ".dll") + ) { + return buildPath; + } + + return join(__dirname, "dist", "crsqlite"); +} + +module.exports.extensionPath = resolve(); diff --git a/core/nodejs-helper.js b/core/nodejs-helper.js index f200f8730..edf3c3983 100644 --- a/core/nodejs-helper.js +++ b/core/nodejs-helper.js @@ -1,4 +1,10 @@ // Exports the path to the cr-sqlite loadable extension. +// +// Resolution order: +// 1. Platform-specific npm package (@anthropic/crsqlite-{os}-{cpu}) +// 2. Local build/ directory (development) +// 3. Local dist/ directory (legacy / manual builds) +// // Usage with node:sqlite (Node >= 22.5): // // import { extensionPath } from '@anthropic/crsqlite'; @@ -13,8 +19,43 @@ // const db = new Database(':memory:'); // db.loadExtension(extensionPath); -import { fileURLToPath } from "node:url"; +import { createRequire } from "node:module"; import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; +import { existsSync } from "node:fs"; const __dirname = dirname(fileURLToPath(import.meta.url)); -export const extensionPath = join(__dirname, "dist", "crsqlite"); +const require = createRequire(import.meta.url); + +const PLATFORM_PACKAGES = { + "linux-x64": "@anthropic/crsqlite-linux-x64", + "darwin-arm64": "@anthropic/crsqlite-darwin-arm64", + "darwin-x64": "@anthropic/crsqlite-darwin-x64", + "win32-x64": "@anthropic/crsqlite-win32-x64", +}; + +function resolve() { + // 1. Try the platform-specific npm package. + const key = `${process.platform}-${process.arch}`; + const pkg = PLATFORM_PACKAGES[key]; + if (pkg) { + try { + return require(pkg).path; + } catch {} + } + + // 2. Local build/ (dev). + const buildPath = join(__dirname, "build", "crsqlite"); + if ( + existsSync(buildPath + ".so") || + existsSync(buildPath + ".dylib") || + existsSync(buildPath + ".dll") + ) { + return buildPath; + } + + // 3. Local dist/ (legacy). + return join(__dirname, "dist", "crsqlite"); +} + +export const extensionPath = resolve(); diff --git a/core/package.json b/core/package.json index 97b7b3ec2..395387f27 100644 --- a/core/package.json +++ b/core/package.json @@ -15,8 +15,7 @@ "files": [ "nodejs-helper.js", "nodejs-helper.cjs", - "nodejs-helper.d.ts", - "dist/" + "nodejs-helper.d.ts" ], "scripts": { "build": "cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build && mkdir -p dist && cp build/crsqlite.* dist/", @@ -26,6 +25,12 @@ "engines": { "node": ">=22.5.0" }, + "optionalDependencies": { + "@anthropic/crsqlite-linux-x64": "0.1.0", + "@anthropic/crsqlite-darwin-arm64": "0.1.0", + "@anthropic/crsqlite-darwin-x64": "0.1.0", + "@anthropic/crsqlite-win32-x64": "0.1.0" + }, "license": "MIT", "repository": { "type": "git", diff --git a/npm/crsqlite-darwin-arm64/index.js b/npm/crsqlite-darwin-arm64/index.js new file mode 100644 index 000000000..fadd289e4 --- /dev/null +++ b/npm/crsqlite-darwin-arm64/index.js @@ -0,0 +1,2 @@ +const { join } = require("node:path"); +module.exports.path = join(__dirname, "crsqlite"); diff --git a/npm/crsqlite-darwin-arm64/package.json b/npm/crsqlite-darwin-arm64/package.json new file mode 100644 index 000000000..93a49f5f2 --- /dev/null +++ b/npm/crsqlite-darwin-arm64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@anthropic/crsqlite-darwin-arm64", + "version": "0.1.0", + "description": "cr-sqlite prebuilt binary for macOS arm64 (Apple Silicon)", + "os": ["darwin"], + "cpu": ["arm64"], + "main": "index.js", + "files": ["index.js", "crsqlite.dylib"], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/shards-lang/cr-sqlite.git", + "directory": "npm/crsqlite-darwin-arm64" + } +} diff --git a/npm/crsqlite-darwin-x64/index.js b/npm/crsqlite-darwin-x64/index.js new file mode 100644 index 000000000..fadd289e4 --- /dev/null +++ b/npm/crsqlite-darwin-x64/index.js @@ -0,0 +1,2 @@ +const { join } = require("node:path"); +module.exports.path = join(__dirname, "crsqlite"); diff --git a/npm/crsqlite-darwin-x64/package.json b/npm/crsqlite-darwin-x64/package.json new file mode 100644 index 000000000..9fed1d718 --- /dev/null +++ b/npm/crsqlite-darwin-x64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@anthropic/crsqlite-darwin-x64", + "version": "0.1.0", + "description": "cr-sqlite prebuilt binary for macOS x64 (Intel)", + "os": ["darwin"], + "cpu": ["x64"], + "main": "index.js", + "files": ["index.js", "crsqlite.dylib"], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/shards-lang/cr-sqlite.git", + "directory": "npm/crsqlite-darwin-x64" + } +} diff --git a/npm/crsqlite-linux-x64/index.js b/npm/crsqlite-linux-x64/index.js new file mode 100644 index 000000000..fadd289e4 --- /dev/null +++ b/npm/crsqlite-linux-x64/index.js @@ -0,0 +1,2 @@ +const { join } = require("node:path"); +module.exports.path = join(__dirname, "crsqlite"); diff --git a/npm/crsqlite-linux-x64/package.json b/npm/crsqlite-linux-x64/package.json new file mode 100644 index 000000000..aeaccee0e --- /dev/null +++ b/npm/crsqlite-linux-x64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@anthropic/crsqlite-linux-x64", + "version": "0.1.0", + "description": "cr-sqlite prebuilt binary for Linux x64", + "os": ["linux"], + "cpu": ["x64"], + "main": "index.js", + "files": ["index.js", "crsqlite.so"], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/shards-lang/cr-sqlite.git", + "directory": "npm/crsqlite-linux-x64" + } +} diff --git a/npm/crsqlite-win32-x64/index.js b/npm/crsqlite-win32-x64/index.js new file mode 100644 index 000000000..fadd289e4 --- /dev/null +++ b/npm/crsqlite-win32-x64/index.js @@ -0,0 +1,2 @@ +const { join } = require("node:path"); +module.exports.path = join(__dirname, "crsqlite"); diff --git a/npm/crsqlite-win32-x64/package.json b/npm/crsqlite-win32-x64/package.json new file mode 100644 index 000000000..05629a122 --- /dev/null +++ b/npm/crsqlite-win32-x64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@anthropic/crsqlite-win32-x64", + "version": "0.1.0", + "description": "cr-sqlite prebuilt binary for Windows x64", + "os": ["win32"], + "cpu": ["x64"], + "main": "index.js", + "files": ["index.js", "crsqlite.dll"], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/shards-lang/cr-sqlite.git", + "directory": "npm/crsqlite-win32-x64" + } +} From 4866d1f49a7cedff1e47fbc810b4f095f9a85b0e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Apr 2026 15:36:00 +0000 Subject: [PATCH 2/3] Switch npm publish to trusted publishing (OIDC provenance) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace NPM_TOKEN secret with GitHub's OIDC id-token. The publish job now uses --provenance, which proves the package was built by this exact workflow and commit. No stored secrets needed — just link each package to this repo on npmjs.com. https://claude.ai/code/session_0156tM6LmjAC3xYz5xwETvym --- .github/workflows/npm-publish.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml index 5709066ed..d57cb8ba8 100644 --- a/.github/workflows/npm-publish.yaml +++ b/.github/workflows/npm-publish.yaml @@ -4,7 +4,8 @@ on: name: "npm-publish" # The release tag must match the version in package.json (e.g. v0.1.0). -# Set NPM_TOKEN as a repository secret to authenticate with npm. +# Uses npm trusted publishing (OIDC provenance) — no NPM_TOKEN secret needed. +# Setup: on npmjs.com, link each package to this repo + workflow file. jobs: build: @@ -82,6 +83,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + id-token: write steps: - uses: actions/checkout@v4 @@ -104,18 +106,14 @@ jobs: done - name: Publish platform packages - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | for platform in linux-x64 darwin-arm64 darwin-x64 win32-x64; do cd npm/crsqlite-${platform} - npm publish --access public || echo "Skipped crsqlite-${platform} (may already exist)" + npm publish --access public --provenance || echo "Skipped crsqlite-${platform} (may already exist)" cd ../.. done - name: Publish main package - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | cd core - npm publish --access public + npm publish --access public --provenance From 6b9999a866633041994117a7f2f97a9459a4f613 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Apr 2026 15:40:35 +0000 Subject: [PATCH 3/3] Rename npm scope from @anthropic to @shards-lang https://claude.ai/code/session_0156tM6LmjAC3xYz5xwETvym --- core/nodejs-helper.cjs | 8 ++++---- core/nodejs-helper.js | 14 +++++++------- core/package.json | 10 +++++----- npm/crsqlite-darwin-arm64/package.json | 2 +- npm/crsqlite-darwin-x64/package.json | 2 +- npm/crsqlite-linux-x64/package.json | 2 +- npm/crsqlite-win32-x64/package.json | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/core/nodejs-helper.cjs b/core/nodejs-helper.cjs index 4c8c0e7e5..266d592fb 100644 --- a/core/nodejs-helper.cjs +++ b/core/nodejs-helper.cjs @@ -3,10 +3,10 @@ const { join } = require("node:path"); const { existsSync } = require("node:fs"); const PLATFORM_PACKAGES = { - "linux-x64": "@anthropic/crsqlite-linux-x64", - "darwin-arm64": "@anthropic/crsqlite-darwin-arm64", - "darwin-x64": "@anthropic/crsqlite-darwin-x64", - "win32-x64": "@anthropic/crsqlite-win32-x64", + "linux-x64": "@shards-lang/crsqlite-linux-x64", + "darwin-arm64": "@shards-lang/crsqlite-darwin-arm64", + "darwin-x64": "@shards-lang/crsqlite-darwin-x64", + "win32-x64": "@shards-lang/crsqlite-win32-x64", }; function resolve() { diff --git a/core/nodejs-helper.js b/core/nodejs-helper.js index edf3c3983..76e2684db 100644 --- a/core/nodejs-helper.js +++ b/core/nodejs-helper.js @@ -1,20 +1,20 @@ // Exports the path to the cr-sqlite loadable extension. // // Resolution order: -// 1. Platform-specific npm package (@anthropic/crsqlite-{os}-{cpu}) +// 1. Platform-specific npm package (@shards-lang/crsqlite-{os}-{cpu}) // 2. Local build/ directory (development) // 3. Local dist/ directory (legacy / manual builds) // // Usage with node:sqlite (Node >= 22.5): // -// import { extensionPath } from '@anthropic/crsqlite'; +// import { extensionPath } from '@shards-lang/crsqlite'; // import { DatabaseSync } from 'node:sqlite'; // const db = new DatabaseSync(':memory:', { allowExtension: true }); // db.loadExtension(extensionPath); // // Usage with better-sqlite3: // -// import { extensionPath } from '@anthropic/crsqlite'; +// import { extensionPath } from '@shards-lang/crsqlite'; // import Database from 'better-sqlite3'; // const db = new Database(':memory:'); // db.loadExtension(extensionPath); @@ -28,10 +28,10 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); const require = createRequire(import.meta.url); const PLATFORM_PACKAGES = { - "linux-x64": "@anthropic/crsqlite-linux-x64", - "darwin-arm64": "@anthropic/crsqlite-darwin-arm64", - "darwin-x64": "@anthropic/crsqlite-darwin-x64", - "win32-x64": "@anthropic/crsqlite-win32-x64", + "linux-x64": "@shards-lang/crsqlite-linux-x64", + "darwin-arm64": "@shards-lang/crsqlite-darwin-arm64", + "darwin-x64": "@shards-lang/crsqlite-darwin-x64", + "win32-x64": "@shards-lang/crsqlite-win32-x64", }; function resolve() { diff --git a/core/package.json b/core/package.json index 395387f27..f104c6c60 100644 --- a/core/package.json +++ b/core/package.json @@ -1,5 +1,5 @@ { - "name": "@anthropic/crsqlite", + "name": "@shards-lang/crsqlite", "version": "0.1.0", "description": "cr-sqlite loadable extension for Node.js — CRDT-based multi-master replication for SQLite", "type": "module", @@ -26,10 +26,10 @@ "node": ">=22.5.0" }, "optionalDependencies": { - "@anthropic/crsqlite-linux-x64": "0.1.0", - "@anthropic/crsqlite-darwin-arm64": "0.1.0", - "@anthropic/crsqlite-darwin-x64": "0.1.0", - "@anthropic/crsqlite-win32-x64": "0.1.0" + "@shards-lang/crsqlite-linux-x64": "0.1.0", + "@shards-lang/crsqlite-darwin-arm64": "0.1.0", + "@shards-lang/crsqlite-darwin-x64": "0.1.0", + "@shards-lang/crsqlite-win32-x64": "0.1.0" }, "license": "MIT", "repository": { diff --git a/npm/crsqlite-darwin-arm64/package.json b/npm/crsqlite-darwin-arm64/package.json index 93a49f5f2..b5d7679ee 100644 --- a/npm/crsqlite-darwin-arm64/package.json +++ b/npm/crsqlite-darwin-arm64/package.json @@ -1,5 +1,5 @@ { - "name": "@anthropic/crsqlite-darwin-arm64", + "name": "@shards-lang/crsqlite-darwin-arm64", "version": "0.1.0", "description": "cr-sqlite prebuilt binary for macOS arm64 (Apple Silicon)", "os": ["darwin"], diff --git a/npm/crsqlite-darwin-x64/package.json b/npm/crsqlite-darwin-x64/package.json index 9fed1d718..c4e92230b 100644 --- a/npm/crsqlite-darwin-x64/package.json +++ b/npm/crsqlite-darwin-x64/package.json @@ -1,5 +1,5 @@ { - "name": "@anthropic/crsqlite-darwin-x64", + "name": "@shards-lang/crsqlite-darwin-x64", "version": "0.1.0", "description": "cr-sqlite prebuilt binary for macOS x64 (Intel)", "os": ["darwin"], diff --git a/npm/crsqlite-linux-x64/package.json b/npm/crsqlite-linux-x64/package.json index aeaccee0e..7969bae11 100644 --- a/npm/crsqlite-linux-x64/package.json +++ b/npm/crsqlite-linux-x64/package.json @@ -1,5 +1,5 @@ { - "name": "@anthropic/crsqlite-linux-x64", + "name": "@shards-lang/crsqlite-linux-x64", "version": "0.1.0", "description": "cr-sqlite prebuilt binary for Linux x64", "os": ["linux"], diff --git a/npm/crsqlite-win32-x64/package.json b/npm/crsqlite-win32-x64/package.json index 05629a122..44cf7ca71 100644 --- a/npm/crsqlite-win32-x64/package.json +++ b/npm/crsqlite-win32-x64/package.json @@ -1,5 +1,5 @@ { - "name": "@anthropic/crsqlite-win32-x64", + "name": "@shards-lang/crsqlite-win32-x64", "version": "0.1.0", "description": "cr-sqlite prebuilt binary for Windows x64", "os": ["win32"],