Contributing to GraphBit - JavaScript/TypeScript¶
Welcome to contributing to GraphBit's JavaScript/TypeScript bindings! This guide covers everything you need to know about developing, testing, and contributing to the JavaScript ecosystem of GraphBit.
Note: This document is specific to JavaScript/TypeScript development. For Rust core or Python contributions, see the main Contributing Guide.
Quick Start for JavaScript/TypeScript Contributors¶
1. Development Setup¶
# Clone the repository
git clone https://github.com/InfinitiBit/graphbit.git
cd graphbit/javascript
# Install dependencies
npm install
# Build the native module
npm run build
# Run tests to verify setup
npm test
2. Project Structure¶
javascript/
โโโ src/ # Rust source for napi-rs bindings
โ โโโ lib.rs # Main binding entry point
โโโ tests/ # TypeScript/JavaScript tests
โโโ examples/ # Example applications
โโโ index.d.ts # TypeScript type definitions
โโโ package.json # Node.js package configuration
โโโ tsconfig.json # TypeScript configuration
โโโ Cargo.toml # Rust dependencies
โโโ build.rs # Build script
Ways to Contribute¶
๐ Bug Reports¶
File JavaScript-specific issues with: - Node.js version (node --version) - Operating system - Package version - Minimal reproducible code example
// Example bug report code
import { init, LlmConfig, Executor } from '@infinitibit_gmbh/graphbit';
init();
const config = LlmConfig.openai({ apiKey: 'test' });
// ... steps to reproduce bug
โจ Feature Requests¶
For JavaScript features: - Describe the JavaScript API you envision - Provide TypeScript interface proposals - Include usage examples - Consider compatibility with Node.js versions
๐ Documentation¶
Improve JavaScript documentation: - Add TypeScript examples - Create tutorials and guides - Document common patterns - Add JSDoc comments
๐งน Code Contributions¶
Follow JavaScript best practices: - Write TypeScript for type safety - Include tests with vitest - Follow ESLint rules - Update type definitions
Development Workflow¶
1. Fork and Clone¶
2. Create Feature Branch¶
3. Development Cycle¶
# Make changes to JavaScript/TypeScript or Rust bindings
# Build the module
npm run build
# Run tests
npm test
# Run specific test file
npm test -- path/to/test.ts
# Run tests in watch mode
npm run test:watch
# Lint code
npm run lint
# Format code
npm run format
4. Commit Changes¶
Follow Conventional Commits: - feat(js): - New feature - fix(js): - Bug fix - docs(js): - Documentation - test(js): - Tests - refactor(js): - Code refactoring - chore(js): - Maintenance
5. Submit Pull Request¶
- Push to your fork
- Create pull request to
main - Fill out PR template
- Wait for CI checks and review
Code Quality Standards¶
TypeScript Code Style¶
// Use explicit types
function processData(input: string): Promise<Result> {
// Implementation
}
// Use interfaces for objects
interface WorkflowConfig {
name: string;
timeout?: number;
}
// Use async/await (not callbacks)
async function executeWorkflow() {
const result = await executor.execute(workflow);
return result;
}
// Handle errors explicitly
try {
const result = await executor.execute(workflow);
} catch (error) {
console.error('Execution failed:', error);
}
Testing Guidelines¶
import { describe, it, expect, beforeEach } from 'vitest';
import { init, LlmConfig, Workflow, Node, Executor } from '@infinitibit_gmbh/graphbit';
describe('Workflow Execution', () => {
beforeEach(() => {
init();
});
it('should execute simple workflow', async () => {
const config = LlmConfig.ollama({ model: 'llama3.2' });
const executor = new Executor(config);
const workflow = new Workflow('Test Workflow');
const node = Node.agent('Test Agent', 'Say hello', 'agent1');
await workflow.addNode(node);
await workflow.validate();
const result = await executor.execute(workflow);
expect(result.isSuccess()).toBe(true);
});
it('should handle errors gracefully', async () => {
// Test error handling
});
});
Documentation Standards¶
/**
* Executes a workflow with the configured LLM.
*
* @param workflow - The workflow to execute
* @returns A promise that resolves to the execution result
* @throws {Error} If workflow validation fails
*
* @example
* ```typescript
* const executor = new Executor(config);
* const result = await executor.execute(workflow);
*
* if (result.isSuccess()) {
* console.log(result.variables());
* }
* ```
*/
async execute(workflow: Workflow): Promise<WorkflowResult>;
Building and Testing¶
Build Commands¶
# Development build
npm run build
# Production build (optimized)
npm run build:release
# Clean build artifacts
npm run clean
# Rebuild from scratch
npm run rebuild
Testing¶
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Run specific test suite
npm test -- --grep "Workflow"
# Run tests in watch mode
npm run test:watch
# Run integration tests
npm run test:integration
Linting and Formatting¶
# Lint TypeScript code
npm run lint
# Fix linting issues
npm run lint:fix
# Format code with Prettier
npm run format
# Check formatting
npm run format:check
Architecture Overview¶
napi-rs Binding Layer¶
GraphBit's JavaScript bindings use napi-rs for Rust-Node.js interop:
// src/lib.rs
#[napi]
pub struct Executor {
inner: Arc<RustExecutor>,
}
#[napi]
impl Executor {
#[napi(constructor)]
pub fn new(config: &LlmConfig) -> napi::Result<Self> {
// Convert JS config to Rust
Ok(Self {
inner: Arc::new(RustExecutor::new(config.inner.clone()))
})
}
#[napi]
pub async fn execute(&self, workflow: &Workflow) -> napi::Result<WorkflowResult> {
// Execute workflow
}
}
TypeScript Type Definitions¶
Types are auto-generated by napi-rs but can be supplemented:
// Custom types in index.d.ts
export interface ExecutorOptions {
timeoutSeconds?: number;
debug?: boolean;
lightweightMode?: boolean;
}
export interface WorkflowResultVariables {
[agentId: string]: string;
}
Async Patterns¶
All I/O operations use async/await:
// Good - async/await
const result = await executor.execute(workflow);
// Avoid - callbacks
executor.execute(workflow, (error, result) => {
// Don't use this pattern
});
Common Development Tasks¶
Adding a New API Method¶
- Add Rust implementation in
src/lib.rs:
#[napi]
impl Executor {
#[napi]
pub async fn new_method(&self, param: String) -> napi::Result<String> {
Ok(format!("Processed: {}", param))
}
}
- Rebuild the module:
- Add tests in
tests/:
it('should call new method', async () => {
const result = await executor.newMethod('test');
expect(result).toBe('Processed: test');
});
- Update documentation in
docs/:
### newMethod(param)
Processes the provided parameter.
**Parameters:**
- `param` (string): The parameter to process
**Returns:** Promise<string>
Adding Examples¶
Create comprehensive examples in examples/:
// examples/new-feature-example.ts
import { init, LlmConfig, Executor } from '@infinitibit_gmbh/graphbit';
async function main() {
init();
const config = LlmConfig.openai({
apiKey: process.env.OPENAI_API_KEY || ''
});
const executor = new Executor(config);
// Demonstrate new feature
console.log('Example output:', await executor.newFeature());
}
main().catch(console.error);
Debugging¶
// Enable debug mode
init({ debug: true, logLevel: 'debug' });
// Use Node.js debugger
// In VS Code launch.json:
{
"type": "node",
"request": "launch",
"name": "Debug Tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["test"],
"console": "integratedTerminal"
}
Performance Considerations¶
Memory Management¶
// Good - let objects be garbage collected
async function processMany() {
for (const item of items) {
const result = await process(item);
// Result goes out of scope after each iteration
}
}
// Avoid - accumulating large objects
async function processMany() {
const results = [];
for (const item of items) {
results.push(await process(item)); // Can accumulate memory
}
return results;
}
Async Best Practices¶
// Good - parallel execution
const results = await Promise.all(
workflows.map(w => executor.execute(w))
);
// Avoid - sequential when parallel is possible
const results = [];
for (const workflow of workflows) {
results.push(await executor.execute(workflow));
}
Security Guidelines¶
API Key Handling¶
// Good - environment variables
const config = LlmConfig.openai({
apiKey: process.env.OPENAI_API_KEY || ''
});
// Never - hardcoded keys
const config = LlmConfig.openai({
apiKey: 'sk-...' // Never commit this!
});
Input Validation¶
function validateInput(input: string): void {
if (typeof input !== 'string') {
throw new TypeError('Input must be a string');
}
if (input.length === 0) {
throw new Error('Input cannot be empty');
}
if (input.length > 100000) {
throw new Error('Input too large');
}
}
CI/CD Integration¶
GitHub Actions Workflow¶
name: JavaScript Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build
- run: npm test
- run: npm run lint
Release Process¶
- Update version in
package.json - Update CHANGELOG.md
- Build release:
- Test release build:
- Publish (maintainers only):
Common Issues and Solutions¶
Build Failures¶
# Clean and rebuild
npm run clean
npm run rebuild
# Ensure Rust toolchain is updated
rustup update
# Check napi-rs CLI version
npx @napi-rs/cli -v
Type Definition Mismatches¶
Test Failures¶
# Run tests with verbose output
npm test -- --reporter=verbose
# Run single test file
npm test -- tests/specific-test.ts
Resources¶
- napi-rs Documentation
- Node.js API Documentation
- TypeScript Handbook
- Vitest Documentation
- GraphBit JavaScript API Reference
Getting Help¶
- Documentation: Check JavaScript docs
- GitHub Issues: Use
javascriptlabel - Discussions: Ask in GitHub discussions
- Examples: Study
examples/directory
Recognition¶
JavaScript contributors are recognized in: - CHANGELOG.md - Package contributors list - GitHub contributors page
Next Steps¶
Ready to contribute? Here are some good first steps:
- Setup: Complete development environment setup
- Explore: Study existing tests and examples
- Build: Try building the project
- Test: Run the test suite
- Contribute: Start with good first issue
Thank you for contributing to GraphBit's JavaScript ecosystem! ๐
For core development or Python contributions, see: - Main Contributing Guide - JavaScript Bindings Architecture - Development Guide