fix: Update ruvector-math-wasm to use @ruvector/math-wasm scoped package

- Rename npm package from ruvector-math-wasm to @ruvector/math-wasm
- Update README with correct scoped package name
- Update workflow to publish with scoped name
- Add scripts/test-wasm.mjs for WASM package testing
- Consistent with @ruvector/attention-* naming convention

Published:
- @ruvector/math-wasm@0.1.31 on npm

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rUv 2026-01-11 17:21:16 +00:00
parent e39f244940
commit 3200ea2578
3 changed files with 188 additions and 10 deletions

View file

@ -258,9 +258,11 @@ jobs:
run: find artifacts -type f -name "*.node" -o -name "*.wasm" | head -50
# --- Publish WASM packages ---
- name: Publish ruvector-math-wasm to npm
- name: Publish @ruvector/math-wasm to npm
run: |
cd artifacts/wasm-ruvector-math-wasm
# Update package name to scoped
jq '.name = "@ruvector/math-wasm"' package.json > tmp.json && mv tmp.json package.json
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@ -462,7 +464,7 @@ jobs:
run: |
echo "## npm Publishing" >> $GITHUB_STEP_SUMMARY
echo "### WASM Packages" >> $GITHUB_STEP_SUMMARY
echo "✅ ruvector-math-wasm" >> $GITHUB_STEP_SUMMARY
echo "✅ @ruvector/math-wasm" >> $GITHUB_STEP_SUMMARY
echo "✅ @ruvector/attention-wasm" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Native Packages" >> $GITHUB_STEP_SUMMARY
@ -528,7 +530,7 @@ jobs:
- `ruvector-attention-wasm` - WASM bindings for attention
#### npm
- `ruvector-math-wasm` - Browser WASM package
- `@ruvector/math-wasm` - Browser WASM package
- `@ruvector/attention` - Main Node.js package (auto-selects platform)
- `@ruvector/attention-wasm` - Browser WASM package
- Platform-specific: linux-x64, linux-arm64, darwin-x64, darwin-arm64, win32-x64
@ -543,7 +545,7 @@ jobs:
npm install @ruvector/attention
# Browser (WASM)
npm install ruvector-math-wasm @ruvector/attention-wasm
npm install @ruvector/math-wasm @ruvector/attention-wasm
```
files: release-assets/*
draft: false

View file

@ -1,6 +1,6 @@
# ruvector-math-wasm
# @ruvector/math-wasm
[![npm version](https://img.shields.io/npm/v/ruvector-math-wasm.svg)](https://www.npmjs.com/package/ruvector-math-wasm)
[![npm version](https://img.shields.io/npm/v/@ruvector/math-wasm.svg)](https://www.npmjs.com/package/@ruvector/math-wasm)
[![crates.io](https://img.shields.io/crates/v/ruvector-math-wasm.svg)](https://crates.io/crates/ruvector-math-wasm)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE)
[![WASM](https://img.shields.io/badge/target-wasm32-orange.svg)](https://webassembly.org/)
@ -21,7 +21,7 @@ Brings Optimal Transport, Information Geometry, and Product Manifolds to the bro
## Installation
```bash
npm install ruvector-math-wasm
npm install @ruvector/math-wasm
# or
yarn add ruvector-math-wasm
# or
@ -37,7 +37,7 @@ import init, {
WasmSlicedWasserstein,
WasmSinkhorn,
WasmProductManifold
} from 'ruvector-math-wasm';
} from '@ruvector/math-wasm';
// Initialize WASM module
await init();
@ -53,7 +53,7 @@ console.log(`Wasserstein distance: ${distance}`);
### Node.js
```javascript
const { WasmSlicedWasserstein } = require('ruvector-math-wasm');
const { WasmSlicedWasserstein } = require('@ruvector/math-wasm');
const sw = new WasmSlicedWasserstein(100);
const dist = sw.distance(source, target, 2);
@ -163,7 +163,7 @@ Benchmarked on M1 MacBook Pro (WASM in Chrome):
Full TypeScript definitions are included:
```typescript
import { WasmSlicedWasserstein, WasmSinkhornConfig } from 'ruvector-math-wasm';
import { WasmSlicedWasserstein, WasmSinkhornConfig } from '@ruvector/math-wasm';
const sw: WasmSlicedWasserstein = new WasmSlicedWasserstein(100);
const distance: number = sw.distance(source, target, dim);

176
scripts/test-wasm.mjs Executable file
View file

@ -0,0 +1,176 @@
#!/usr/bin/env node
/**
* WASM Package Test Script
* Tests ruvector-math-wasm and ruvector-attention-wasm in Node.js
*/
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
console.log('🧪 Testing RuVector WASM Packages\n');
// ============================================================================
// Test ruvector-math-wasm
// ============================================================================
async function testMathWasm() {
console.log('📦 Testing ruvector-math-wasm...');
const pkgPath = join(__dirname, '../crates/ruvector-math-wasm/pkg');
try {
// Load WASM module
const wasmPath = join(pkgPath, 'ruvector_math_wasm_bg.wasm');
const wasmBuffer = readFileSync(wasmPath);
// Import the JS bindings
const mathWasm = await import(join(pkgPath, 'ruvector_math_wasm.js'));
// Initialize with WASM bytes
await mathWasm.default(wasmBuffer);
// Test 1: Sliced Wasserstein Distance
console.log(' ├─ Testing SlicedWasserstein...');
const sw = new mathWasm.WasmSlicedWasserstein(100);
// Create test point clouds (3 points in 2D each)
const source = new Float64Array([0, 0, 1, 0, 0, 1]);
const target = new Float64Array([2, 0, 3, 0, 2, 1]);
const distance = sw.distance(source, target, 2);
console.log(` │ Distance: ${distance.toFixed(4)}`);
if (distance > 0 && distance < 10) {
console.log(' │ ✅ SlicedWasserstein works!');
} else {
throw new Error(`Unexpected distance: ${distance}`);
}
// Test 2: Product Manifold
console.log(' ├─ Testing ProductManifold...');
const manifold = new mathWasm.WasmProductManifold(4, 2, 2); // E^4 x H^2 x S^2
// Create test points (8D total)
const pointA = new Float64Array([1, 0, 0, 0, 0.1, 0.1, 1, 0]);
const pointB = new Float64Array([0, 1, 0, 0, 0.2, 0.1, 0, 1]);
const manifoldDist = manifold.distance(pointA, pointB);
console.log(` │ Manifold distance: ${manifoldDist.toFixed(4)}`);
if (manifoldDist > 0) {
console.log(' │ ✅ ProductManifold works!');
} else {
throw new Error(`Unexpected manifold distance: ${manifoldDist}`);
}
// Test 3: Spherical Space
console.log(' ├─ Testing SphericalSpace...');
const sphere = new mathWasm.WasmSphericalSpace(3);
const vecA = new Float64Array([1, 0, 0]);
const vecB = new Float64Array([0, 1, 0]);
const sphereDist = sphere.distance(vecA, vecB);
console.log(` │ Spherical distance: ${sphereDist.toFixed(4)} (expected: ~1.5708 = π/2)`);
if (Math.abs(sphereDist - Math.PI/2) < 0.01) {
console.log(' │ ✅ SphericalSpace works!');
} else {
throw new Error(`Unexpected spherical distance: ${sphereDist}`);
}
console.log(' └─ ✅ ruvector-math-wasm: All tests passed!\n');
return true;
} catch (error) {
console.error(' └─ ❌ Error:', error.message);
return false;
}
}
// ============================================================================
// Test ruvector-attention-wasm
// ============================================================================
async function testAttentionWasm() {
console.log('📦 Testing ruvector-attention-wasm...');
const pkgPath = join(__dirname, '../crates/ruvector-attention-wasm/pkg');
try {
// Check if pkg exists (need to build first)
const wasmPath = join(pkgPath, 'ruvector_attention_wasm_bg.wasm');
let wasmBuffer;
try {
wasmBuffer = readFileSync(wasmPath);
} catch {
console.log(' └─ ⚠️ Package not built. Building now...');
const { execSync } = await import('child_process');
execSync('wasm-pack build crates/ruvector-attention-wasm --target web --release', {
cwd: join(__dirname, '..'),
stdio: 'inherit'
});
wasmBuffer = readFileSync(wasmPath);
}
// Import the JS bindings
const attentionWasm = await import(join(pkgPath, 'ruvector_attention_wasm.js'));
// Initialize with WASM bytes
await attentionWasm.default(wasmBuffer);
// Test 1: Scaled Dot Product Attention
console.log(' ├─ Testing ScaledDotProductAttention...');
if (attentionWasm.WasmScaledDotProductAttention) {
const attention = new attentionWasm.WasmScaledDotProductAttention(64);
console.log(' │ ✅ ScaledDotProductAttention initialized');
} else {
console.log(' │ ⚠️ ScaledDotProductAttention not exported');
}
// Test 2: Flash Attention (if available)
console.log(' ├─ Testing FlashAttention...');
if (attentionWasm.WasmFlashAttention) {
const flash = new attentionWasm.WasmFlashAttention(64, 64);
console.log(' │ ✅ FlashAttention initialized');
} else {
console.log(' │ ⚠️ FlashAttention not exported');
}
// List available exports
console.log(' ├─ Available exports:');
const exports = Object.keys(attentionWasm).filter(k => k.startsWith('Wasm'));
exports.forEach(e => console.log(` │ - ${e}`));
console.log(' └─ ✅ ruvector-attention-wasm: Package loaded successfully!\n');
return true;
} catch (error) {
console.error(' └─ ❌ Error:', error.message);
return false;
}
}
// ============================================================================
// Run all tests
// ============================================================================
async function main() {
const results = {
math: await testMathWasm(),
attention: await testAttentionWasm()
};
console.log('═══════════════════════════════════════');
console.log('📊 Test Results:');
console.log(` ruvector-math-wasm: ${results.math ? '✅ PASS' : '❌ FAIL'}`);
console.log(` ruvector-attention-wasm: ${results.attention ? '✅ PASS' : '❌ FAIL'}`);
console.log('═══════════════════════════════════════');
process.exit(results.math && results.attention ? 0 : 1);
}
main().catch(console.error);