diff --git a/.github/workflows/npm-publish.yaml b/.github/workflows/npm-publish.yaml new file mode 100644 index 000000000..d57cb8ba8 --- /dev/null +++ b/.github/workflows/npm-publish.yaml @@ -0,0 +1,119 @@ +on: + release: + types: [published] +name: "npm-publish" + +# The release tag must match the version in package.json (e.g. v0.1.0). +# 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: + 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 + id-token: write + 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 + run: | + for platform in linux-x64 darwin-arm64 darwin-x64 win32-x64; do + cd npm/crsqlite-${platform} + npm publish --access public --provenance || echo "Skipped crsqlite-${platform} (may already exist)" + cd ../.. + done + + - name: Publish main package + run: | + cd core + npm publish --access public --provenance diff --git a/core/nodejs-helper.cjs b/core/nodejs-helper.cjs index 427cc8b4c..266d592fb 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": "@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() { + 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..76e2684db 100644 --- a/core/nodejs-helper.js +++ b/core/nodejs-helper.js @@ -1,20 +1,61 @@ // Exports the path to the cr-sqlite loadable extension. +// +// Resolution order: +// 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); -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": "@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() { + // 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..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", @@ -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": { + "@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": { "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..b5d7679ee --- /dev/null +++ b/npm/crsqlite-darwin-arm64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@shards-lang/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..c4e92230b --- /dev/null +++ b/npm/crsqlite-darwin-x64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@shards-lang/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..7969bae11 --- /dev/null +++ b/npm/crsqlite-linux-x64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@shards-lang/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..44cf7ca71 --- /dev/null +++ b/npm/crsqlite-win32-x64/package.json @@ -0,0 +1,15 @@ +{ + "name": "@shards-lang/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" + } +}