@merl1231/neofs-sdk-ts-rn
v0.1.0
Published
TypeScript SDK for NeoFS - React Native Edition
Maintainers
Readme
NeoFS TypeScript SDK for React Native
A React Native TypeScript SDK for NeoFS - a decentralized, distributed object storage network.
Features
- React Native Compatible: Works seamlessly with React Native via
@merl1231/grpc-react-native - User-Friendly API: Simple, intuitive methods for common operations
- Full NeoFS Support: Containers, objects, sessions, EACL, bearer tokens
- Type Safety: Complete TypeScript definitions
- Cross-Platform: Works on both iOS and Android
Installation
npm install neofs-sdk-ts-rn @merl1231/grpc-react-nativeiOS Setup
cd ios && pod installAndroid Setup
No additional setup required.
Quick Start
import { ReactNativeNeoFSClient, BasicACL, ECDSASigner } from 'neofs-sdk-ts-rn';
// Create a signer from your private key
const signer = ECDSASigner.fromHex(privateKeyHex);
// Initialize the client
const client = new ReactNativeNeoFSClient({
host: 'grpc.testnet.neofs.io',
port: 8082,
signer,
});
// Connect to the network
await client.connect();
try {
// Create a container
const containerId = await client.container().create({
name: 'my-files',
basicAcl: BasicACL.PUBLIC_READ,
placementPolicy: 'REP 3',
});
// Upload a file
const objectId = await client.object().upload({
containerId,
payload: new TextEncoder().encode('Hello, NeoFS!'),
filename: 'hello.txt',
contentType: 'text/plain',
});
console.log('Uploaded object:', objectId);
// Download the file
const { info, payload } = await client.object().download(containerId, objectId);
console.log('Content:', new TextDecoder().decode(payload));
} finally {
await client.disconnect();
}API Overview
Client Initialization
import { ReactNativeNeoFSClient, ECDSASigner } from 'neofs-sdk-ts-rn';
// From hex private key
const signer = ECDSASigner.fromHex('your-private-key-hex');
// Or generate a new key pair
const signer = ECDSASigner.generate();
const client = new ReactNativeNeoFSClient({
host: 'grpc.testnet.neofs.io',
port: 8082,
signer,
useTls: false, // Optional: use TLS
timeout: 30000, // Optional: request timeout
});
await client.connect();Container Operations
import { BasicACL } from 'neofs-sdk-ts-rn';
// Create a container
const containerId = await client.container().create({
name: 'my-files',
basicAcl: BasicACL.PUBLIC_READ,
placementPolicy: 'REP 2',
attributes: [
{ key: 'Project', value: 'MyApp' },
],
});
// Get container info
const info = await client.container().getInfo(containerId);
console.log('Name:', info?.name);
console.log('ACL:', info?.basicAcl.toString(16));
// List all containers
const containers = await client.container().listWithInfo();
// Delete a container
await client.container().remove(containerId);Object Operations
// Upload an object
const objectId = await client.object().upload({
containerId,
payload: imageBuffer,
filename: 'photo.jpg',
contentType: 'image/jpeg',
attributes: [
{ key: 'Album', value: 'Vacation 2024' },
],
});
// Download an object
const { info, payload } = await client.object().download(containerId, objectId);
// Get metadata only
const info = await client.object().getInfo(containerId, objectId);
// Delete an object
await client.object().remove(containerId, objectId);
// Search objects
import { MatchType } from 'neofs-sdk-ts-rn';
const objectIds = await client.object().find({
containerId,
filters: [
{ key: 'ContentType', value: 'image/', matchType: MatchType.COMMON_PREFIX },
],
});
// Paginated search
const { objects, cursor } = await client.object().findPaginated({
containerId,
limit: 50,
});Network Information
// Get current epoch
const epoch = await client.netmap().getCurrentEpoch();
// Get network info
const network = await client.netmap().getNetwork();
console.log('Magic:', network.magicNumber);
// Get all nodes
const nodes = await client.netmap().getNodes();Accounting
const balance = await client.accounting().getBalance();
console.log(`Balance: ${balance.value} (precision: ${balance.precision})`);Session Management
const currentEpoch = await client.netmap().getCurrentEpoch();
const session = await client.session().createSession(currentEpoch + 100n);
console.log('Session ID:', session.id);EACL (Extended Access Control)
import { Table, Target, Record, Operation, publicReadEACL } from 'neofs-sdk-ts-rn';
// Quick preset
const eacl = publicReadEACL(containerId);
// Custom rules
const customEacl = new Table(containerId)
.allowRead([Target.others()])
.denyWrite([Target.others()])
.allow(Operation.PUT, [Target.userId(friendId)]);Bearer Tokens
import { BearerToken, publicReadEACL } from 'neofs-sdk-ts-rn';
const token = new BearerToken()
.setEACL(publicReadEACL(containerId))
.forUser(friendUserId)
.setIssuer(myUserId)
.setLifetime({
iat: currentEpoch,
nbf: currentEpoch,
exp: currentEpoch + 100n,
})
.sign(signer);
const tokenBytes = token.serialize();Waiter (Async Confirmation)
import { Waiter } from 'neofs-sdk-ts-rn';
const waiter = new Waiter(client);
// Create container and wait for it to exist
const containerId = await waiter.containerPut({
name: 'my-container',
basicAcl: BasicACL.PUBLIC_READ,
});
// Upload object and wait for confirmation
const objectId = await waiter.objectPut({
containerId,
payload: data,
});BasicACL Presets
| Preset | Value | Description |
|--------|-------|-------------|
| BasicACL.PRIVATE | 0x1C8C8CCC | Only owner has access |
| BasicACL.PUBLIC_READ | 0x1FBF8CFF | Anyone can read, owner can write |
| BasicACL.PUBLIC_READ_WRITE | 0x1FBFBFFF | Anyone can read and write |
| BasicACL.PUBLIC_APPEND | 0x1FBF9FFF | Anyone can read/append, owner can delete |
Search MatchTypes
| Type | Value | Description |
|------|-------|-------------|
| STRING_EQUAL | 0 | Exact string match |
| STRING_NOT_EQUAL | 1 | String does not match |
| NOT_PRESENT | 2 | Attribute not present |
| COMMON_PREFIX | 3 | String starts with |
| NUM_GT | 4 | Numeric greater than |
| NUM_GE | 5 | Numeric greater or equal |
| NUM_LT | 6 | Numeric less than |
| NUM_LE | 7 | Numeric less or equal |
Error Handling
try {
const data = await client.object().download(containerId, objectId);
} catch (error) {
if (error.message.includes('NOT_FOUND')) {
console.log('Object not found');
} else if (error.message.includes('ACCESS_DENIED')) {
console.log('Permission denied');
} else {
throw error;
}
}Configuration
interface ReactNativeClientConfig {
/** gRPC host (without port) */
host: string;
/** gRPC port */
port: number;
/** Signer for authentication */
signer: Signer;
/** Use TLS (default: false) */
useTls?: boolean;
/** Request timeout in ms (default: 30000) */
timeout?: number;
}Example App
See the example directory for a complete React Native app:
cd example
npm install
npx react-native run-ios
# or
npx react-native run-androidArchitecture
src/
├── client/
│ └── react-native/ # Client implementation
│ ├── client.ts # Main client
│ ├── object-client.ts
│ ├── container-client.ts
│ ├── netmap-client.ts
│ ├── accounting-client.ts
│ ├── session-client.ts
│ └── reputation-client.ts
├── eacl/ # Extended ACL
├── bearer/ # Bearer tokens
├── waiter/ # Async confirmation
└── gen-grpc-react-native/ # Generated protobuf typesCrypto Package
import { ECDSASigner, ECDSASignerRFC6979 } from 'neofs-sdk-ts-rn';
// ECDSA with SHA-512 (FIPS 186-3)
const signer = ECDSASigner.generate();
// Deterministic ECDSA with SHA-256 (RFC 6979)
const signerRFC = ECDSASignerRFC6979.generate();
// Sign data
const signature = signer.sign(data);
// Get public key
const publicKey = signer.public();
// Get user ID (owner ID)
const userId = signer.userId();Development
# Build
npm run build
# Run tests
npm test
# Generate protobuf types
npm run generate:all
# Lint
npm run lint
# Format
npm run formatTroubleshooting
iOS Build Issues
If you encounter build issues on iOS:
cd ios
pod deintegrate
pod installAndroid Connection Issues
Ensure your AndroidManifest.xml has internet permission:
<uses-permission android:name="android.permission.INTERNET" />For local development, you may need to add:
<application
android:usesCleartextTraffic="true"
...>License
Apache 2.0 - see LICENSE for details.
