Skip to content

Ratio1/edge-sdk-ts

Repository files navigation

@ratio1/edge-sdk-ts

A comprehensive SDK for interacting with Ratio1 Edge Node services including CStore (distributed key-value store) and R1FS (distributed file system).

npm version License: MIT

Features

  • πŸ”„ Universal Compatibility: Works in both Node.js and browser environments
  • πŸš€ SSR Support: Full Next.js Server-Side Rendering compatibility
  • πŸ“¦ Optimized Bundles: Separate builds for Node.js and browser with minimal bundle size
  • πŸ”§ TypeScript Support: Full TypeScript definitions included
  • 🌐 Environment Configuration: Flexible configuration via environment variables or direct options
  • πŸ›‘οΈ Error Handling: Comprehensive error handling with detailed error messages
  • πŸ“ File Operations: Complete file upload/download capabilities with base64 support
  • πŸ”Œ Pluggable HTTP Layer: Provide custom fetch and FormData implementations
  • 🌐 Protocol Flexibility: URLs without a scheme automatically default to http://

Installation

npm install @ratio1/edge-sdk-ts

The SDK exports two factory functions:

  • createEdgeSdk for Node.js environments
  • createEdgeSdkBrowserClient for browsers or frameworks like Next.js

Both produce the same API surface so you can share code between environments.

Quick Start

Node.js Environment

import createEdgeSdk from '@ratio1/edge-sdk-ts'

const ratio1 = createEdgeSdk()

// Store a value (values must be stringified JSON)
await ratio1.cstore.setValue({
  key: 'user:123:preferences',
  value: JSON.stringify({ theme: 'dark', language: 'en' })
})

// Retrieve the value
const result = await ratio1.cstore.getValue({
  key: 'user:123:preferences'
})
console.log(JSON.parse(result.result)) // { theme: 'dark', language: 'en' }

Browser Environment (Next.js, React, etc.)

import { createEdgeSdkBrowserClient } from '@ratio1/edge-sdk-ts/browser'

const ratio1 = createEdgeSdkBrowserClient()

// Use the same API as Node.js
const status = await ratio1.cstore.getStatus()

Both creation functions accept the same options. Use the verbose flag to enable debug logging.

const ratio1 = createEdgeSdk({
  verbose: true
})

The SDK also lets you inject a custom fetch implementation and FormData constructor if needed.

Configuration

Environment Variables

The SDK automatically reads configuration from environment variables. These variables are typically injected into the container environment by the Edge Node deployment system:

Node.js Environment:

# These are automatically injected by the Edge Node container environment
export EE_CHAINSTORE_API_URL=http://localhost:31234
export EE_R1FS_API_URL=http://localhost:31235

Next.js Configuration

Run in your application:

import { createEdgeSdkBrowserClient } from '@ratio1/edge-sdk-ts/browser'

const ratio1 = createEdgeSdkBrowserClient()

API Reference

CStore Client

CStore provides a distributed key-value store with hash operations.

Important: All values must be stringified JSON. The CStore service stores everything as JSON strings, so you need to use JSON.stringify() when setting values and JSON.parse() when retrieving them.

Basic Operations

// Get service status
const status = await ratio1.cstore.getStatus()
// Returns: { success: true, result: true, keys?: string[] }

// Set a value (values must be stringified JSON)
await ratio1.cstore.setValue({
  key: 'my-key',
  value: JSON.stringify('my-value')
})
// Returns: { success: true, result: true }

// Get a value
const result = await ratio1.cstore.getValue({
  key: 'my-key'
})
// Returns: { success: true, result: '"my-value"' }
// Note: result is a JSON string, parse it to get the actual value

Hash Operations

// Set a hash field (values must be stringified JSON)
await ratio1.cstore.hset({
  hkey: 'user:123',
  key: 'name',
  value: JSON.stringify('John Doe')
})

// Get a hash field
const name = await ratio1.cstore.hget({
  hkey: 'user:123',
  key: 'name'
})
// Returns: { success: true, result: '"John Doe"' }
// Note: result is a JSON string, parse it to get the actual value

// Get all hash fields
const allFields = await ratio1.cstore.hgetall({
  hkey: 'user:123'
})
// Returns: { success: true, result: { keys: ['name', 'email', 'age'] } }

Advanced Usage Examples

// Store complex objects (must be stringified)
await ratio1.cstore.setValue({
  key: 'user:123:profile',
  value: JSON.stringify({
    name: 'John Doe',
    email: 'john@example.com',
    preferences: {
      theme: 'dark',
      notifications: true
    }
  })
})

// Store arrays (must be stringified)
await ratio1.cstore.setValue({
  key: 'user:123:posts',
  value: JSON.stringify(['post1', 'post2', 'post3'])
})

// Hash operations for user data (values must be stringified)
await ratio1.cstore.hset({
  hkey: 'user:123',
  key: 'name',
  value: JSON.stringify('John')
})
await ratio1.cstore.hset({
  hkey: 'user:123',
  key: 'email',
  value: JSON.stringify('john@example.com')
})
await ratio1.cstore.hset({
  hkey: 'user:123',
  key: 'age',
  value: JSON.stringify(30)
})

// Retrieve all user data
const userData = await ratio1.cstore.hgetall({ hkey: 'user:123' })

R1FS Client

R1FS provides a distributed file system with upload and download capabilities.

File Upload

// Upload a file using FormData (browser)
const fileInput = document.getElementById('file') as HTMLInputElement
const file = fileInput.files[0]

const formData = new FormData()
formData.append('file', file)
formData.append('filename', file.name)
formData.append('secret', 'optional-secret-key')

const uploadResult = await ratio1.r1fs.addFile({ formData })
// Returns: { success: true, result: { message: 'File uploaded', cid: 'QmHash...' } }

// Upload using base64 (works in both Node.js and browser)
const base64Data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...'
const uploadResult = await ratio1.r1fs.addFileBase64({
  file_base64_str: base64Data,
  filename: 'image.png',
  secret: 'optional-secret-key'
})

Streaming Uploads (Node.js & Next.js)

import fs from 'node:fs'
import createEdgeSdk from '@ratio1/edge-sdk-ts'

const ratio1 = createEdgeSdk()

const stream = fs.createReadStream('/tmp/report.csv')

const upload = await ratio1.r1fs.addFile({
  file: stream,
  filename: 'report.csv',
  contentType: 'text/csv',
  secret: process.env.R1FS_SECRET
})

console.log(upload.cid)
// app/api/upload/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server'
import { Readable } from 'node:stream'
import createEdgeSdk from '@ratio1/edge-sdk-ts'

export async function POST(req: NextRequest) {
  const form = await req.formData()
  const file = form.get('file')

  if (!(file instanceof File)) {
    return NextResponse.json({ error: 'file field missing' }, { status: 400 })
  }

  const ratio1 = createEdgeSdk()
  const readable = Readable.fromWeb(file.stream())

  const result = await ratio1.r1fs.addFile({
    file: readable,
    filename: file.name,
    contentType: file.type,
    secret: form.get('secret')?.toString()
  })

  return NextResponse.json({ cid: result.cid })
}

File Download

// Download file as blob/response
const fileResponse = await ratio1.r1fs.getFile({
  cid: 'QmHash...',
  secret: 'optional-secret-key' // if file was uploaded with secret
})

// For browser: create download link
const blob = await fileResponse.blob()
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'filename.ext'
a.click()

// Download as base64
const base64Result = await ratio1.r1fs.getFileBase64({
  cid: 'QmHash...',
  secret: 'optional-secret-key'
})
// Returns: { file_base64_str: 'data:image/png;base64,...', filename: 'image.png' }

Service Status

// Check R1FS service status
const status = await ratio1.r1fs.getStatus()
// Returns: { EE_ID: 'edge-node-id', ... }

Complete File Management Example

// Upload multiple files
async function uploadFiles(files: File[]) {
  const uploadPromises = files.map(async (file) => {
    const formData = new FormData()
    formData.append('file', file)
    formData.append('filename', file.name)

    return await ratio1.r1fs.addFile({ formData })
  })

  const results = await Promise.all(uploadPromises)
  return results.map((r) => r.result.cid)
}

// Download and display image
async function displayImage(cid: string) {
  const response = await ratio1.r1fs.getFile({ cid })
  const blob = await response.blob()
  const url = URL.createObjectURL(blob)

  const img = document.createElement('img')
  img.src = url
  document.body.appendChild(img)
}

Error Handling

The SDK provides comprehensive error handling:

try {
  const result = await ratio1.cstore.getValue({ key: 'non-existent-key' })
  console.log(result.result)
} catch (error) {
  if (error.message.includes('404')) {
    console.log('Key not found')
  } else if (error.message.includes('500')) {
    console.log('Server error')
  } else {
    console.log('Network error:', error.message)
  }
}

Bundle Size Optimization

The package is optimized for different environments:

Environment Bundle Size Dependencies
Node.js ~50KB Includes cross-fetch, form-data
Browser ~15KB Uses native APIs only

Tree Shaking

The package is marked as sideEffects: false, enabling optimal tree shaking:

// Only CStore functionality will be included
import { CStoreClient } from '@ratio1/edge-sdk-ts'

// Only R1FS functionality will be included
import { R1FSClient } from '@ratio1/edge-sdk-ts'

Development

Setup

# Clone the repository
git clone <repository-url>
cd edge-sdk-ts

# Install dependencies
npm install

# Build for both Node.js and browser
npm run build

# Run tests
npm test

# Run specific test suites
npm run test:e2e:cstore
npm run test:e2e:r1fs

Testing

# Run all tests
npm test

# Run specific test files
npm test -- src/__tests__/cstoreClient.test.ts
npm test -- src/__tests__/r1fsClient.test.ts

# Run end-to-end tests
npm run test:e2e:cstore
npm run test:e2e:r1fs

Examples

Next.js Application

// pages/api/upload.ts
import createEdgeSdk from '@ratio1/edge-sdk-ts'

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' })
  }

  const ratio1 = createEdgeSdk()

  try {
    // Store metadata (must be stringified)
    await ratio1.cstore.setValue({
      key: `upload:${Date.now()}`,
      value: JSON.stringify({
        filename: req.body.filename,
        size: req.body.size,
        uploadedAt: new Date().toISOString()
      })
    })

    res.status(200).json({ success: true })
  } catch (error) {
    res.status(500).json({ error: error.message })
  }
}

React Component

// components/FileUpload.tsx
import { createEdgeSdkBrowserClient } from "@ratio1/edge-sdk-ts/browser";
import { useState } from "react";

export function FileUpload() {
	const [uploading, setUploading] = useState(false);
	const [uploadedFiles, setUploadedFiles] = useState<string[]>([]);

	const ratio1 = createEdgeSdkBrowserClient();

	const handleUpload = async (files: FileList) => {
		setUploading(true);

		try {
			const uploadPromises = Array.from(files).map(async (file) => {
				const formData = new FormData();
				formData.append("file", file);
				formData.append("filename", file.name);

				const result = await ratio1.r1fs.addFile({ formData });
				return result.result.cid;
			});

			const cids = await Promise.all(uploadPromises);
			setUploadedFiles((prev) => [...prev, ...cids]);
		} catch (error) {
			console.error("Upload failed:", error);
		} finally {
			setUploading(false);
		}
	};

	return (
		<div>
			<input
				type="file"
				multiple
				onChange={(e) => e.target.files && handleUpload(e.target.files)}
				disabled={uploading}
			/>
			{uploading && <p>Uploading...</p>}
			{uploadedFiles.length > 0 && (
				<div>
					<h3>Uploaded Files:</h3>
					<ul>
						{uploadedFiles.map((cid, index) => (
							<li key={index}>{cid}</li>
						))}
					</ul>
				</div>
			)}
		</div>
	);
}

Browser Compatibility

  • Modern Browsers: Chrome 60+, Firefox 55+, Safari 11+, Edge 79+
  • Node.js: 14.0+
  • Next.js: 12.0+
  • React: 16.8+

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Support

  • πŸ“– Documentation: This README and inline code comments
  • πŸ› Issues: GitHub Issues

About

A client SDK for interacting with Edge Node services

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •