npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@smarthivelabs-devs/storage-react-native

v0.2.1

Published

React Native and Expo SDK for SmartHive Storage.

Readme

@smarthivelabs-devs/storage-react-native

React Native and Expo SDK for SmartHive Storage. Uses URI-based file objects and platform APIs instead of browser File/Blob, making it compatible with React Native's networking layer.

Install

npm install @smarthivelabs-devs/storage-react-native

No peer dependency on the core SDK — this package is self-contained.

Setup

import { SmartHiveReactNativeStorageClient } from '@smarthivelabs-devs/storage-react-native';

const client = new SmartHiveReactNativeStorageClient({
  baseUrl: 'https://storage.yourapp.com', // must start with http:// or https://
  appCode: 'my-app',
  apiKey: 'sk_live_...',
  timeoutMs: 30_000, // optional, recommended for mobile networks
});

Config options

| Option | Type | Required | Description | |--------|------|----------|-------------| | baseUrl | string | Yes | Must be a valid http:// or https:// URL | | appCode | string | Yes | App code from admin console | | apiKey | string | Yes | API key credential | | fetch | typeof fetch | No | Custom fetch implementation | | timeoutMs | number | No | Global request timeout in ms |

The constructor throws immediately if baseUrl, appCode, or apiKey are missing, whitespace-only, or if baseUrl is not a valid HTTP/HTTPS URL.


Upload from Expo ImagePicker

import * as ImagePicker from 'expo-image-picker';

async function pickAndUpload() {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: 'Images',
    quality: 0.8,
  });

  if (result.canceled) return;

  const uploaded = await client.uploadImageAsset({
    bucket: 'avatars',
    asset: result.assets[0],
    visibility: 'PRIVATE',
    ownerUserId: currentUserId,
  });

  console.log('Uploaded:', uploaded.url);
  return uploaded.fileId;
}

uploadImageAsset accepts any asset from expo-image-picker. It falls back to image/jpeg and a timestamped filename when the asset has no mimeType or fileName.


Upload from Expo DocumentPicker

import * as DocumentPicker from 'expo-document-picker';

async function pickDocument() {
  const result = await DocumentPicker.getDocumentAsync({ type: '*/*' });

  if (result.canceled) return;

  const uploaded = await client.uploadDocument({
    bucket: 'attachments',
    document: result.assets[0],
    visibility: 'PRIVATE',
  });

  return uploaded.fileId;
}

Upload from a Custom URI

Use uploadFromUri when you have a URI from any source (camera, filesystem, deep link, etc.):

const uploaded = await client.uploadFromUri({
  bucket: 'photos',
  uri: 'file:///var/mobile/Containers/Data/.../image.jpg',
  filename: 'photo.jpg',
  contentType: 'image/jpeg',
  visibility: 'PRIVATE',
  tenantId: 'tenant-123',
  ownerUserId: 'user-456',
});

Displaying Private Files

Your mobile app should never call getSignedUrl directly — that requires your API key, which must not be embedded in the app bundle. Instead, add a route on your own backend that generates the signed URL after verifying the user has permission, then fetch that route from mobile.

// 1. Your backend (e.g. Next.js route handler) generates the URL:
//    POST /api/files/:id/signed-url  →  { url: "https://storage.…?token=shat_…" }

// 2. Mobile: fetch the signed URL from YOUR API endpoint
async function getPrivateFileUrl(fileId: string): Promise<string> {
  const res = await fetch(`https://api.yourapp.com/files/${fileId}/signed-url`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${await getAuthToken()}` },
  });
  const { url } = await res.json() as { url: string };
  return url;
}

// 3. Use the URL directly — no SmartHive headers needed
const url = await getPrivateFileUrl(fileId);

// Display in Image component
<Image source={{ uri: url }} style={{ width: 300, height: 300 }} />

// Or download via expo-file-system
import * as FileSystem from 'expo-file-system';
const { uri } = await FileSystem.downloadAsync(url, FileSystem.documentDirectory + 'file.jpg');

The signed URL is valid for the expiresInSeconds window your server configured (e.g. 5 minutes). Fetch a fresh one if it has expired.

Read Operations

// Get file metadata
const meta = await client.getMetadata(fileId);
console.log(meta.url); // absolute URL

// Get just the delivery URL
const { url } = await client.getUrl(fileId);

// Build a direct download URL (no network request)
const downloadUrl = client.downloadUrl(fileId);

Delete

await client.delete(fileId);
// { fileId, status: 'DELETED' }

Timeout Configuration

Mobile networks can be slow. Set timeoutMs at the client level or per-request basis is not yet exposed in the React Native client — set it globally in the constructor:

const client = new SmartHiveReactNativeStorageClient({
  baseUrl: 'https://storage.yourapp.com',
  appCode: 'my-app',
  apiKey: 'sk_live_...',
  timeoutMs: 60_000, // 60 seconds for large file uploads
});

Timed-out requests throw a StorageReactNativeError with a message indicating the timeout.


Error Handling

import { StorageReactNativeError } from '@smarthivelabs-devs/storage-react-native';

try {
  await client.uploadFromUri({ bucket: 'photos', uri, filename: 'img.jpg', contentType: 'image/jpeg' });
} catch (err) {
  if (err instanceof StorageReactNativeError) {
    console.error(`Upload failed (HTTP ${err.status}):`, err.message);
    console.error('Payload:', err.payload);
  }
}

Security Notes

  • Use https:// in production. http:// is accepted for local development only.
  • Keep your apiKey in Expo's EXPO_PUBLIC_* env vars for client-side apps, or better yet, fetch it from your own backend at login time.
  • Never hardcode API keys directly in source code — they end up in the app bundle.