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

@bounded-sh/client

v0.0.40

Published

JavaScript SDK for Bounded

Readme

Bounded JS SDK

Bounded is a provable realtime backend for web and React Native applications.

Welcome to the Bounded JavaScript SDK. This SDK lets JavaScript and TypeScript applications authenticate users, read and write policy-governed data, subscribe to realtime updates, and call Bounded services.

Table of Contents

Installation

Install the SDK via npm:

npm install @bounded-sh/client

Or with Yarn:

yarn add @bounded-sh/client

Getting Started

Initialization

Before using the SDK, initialize it with your Application ID. Hosted Bounded Auth email login is the default human-login provider.

import { init } from '@bounded-sh/client';

await init({ appId: 'YOUR_APP_ID' });

Authentication

Human login runs through the hosted redirect or popup helpers, which bind the returned token to the app id and registered redirect URI. Inline app-origin OTP helpers are retired.

import { loginWithRedirect, completeLoginFromRedirect } from '@bounded-sh/client';

// Start hosted login:
await loginWithRedirect({ redirectUri: 'https://yourapp.com/auth/callback', methods: ['email'] });

// On your callback page, on load:
const user = await completeLoginFromRedirect();

You can also listen for authentication state changes:

import { onAuthStateChanged } from '@bounded-sh/client';

const unsubscribe = onAuthStateChanged((user) => {
	if (user) {
		console.log('User logged in:', user.address);
	} else {
		console.log('User logged out');
	}
});

// Later:
unsubscribe();

Embedded wallets — signing

When an app enables embedded wallets in policy.json ("auth": { "wallets": true }), every email login also gets a non-custodial Solana wallet (@user.address). That user can approve transactions with it — signing runs in a Bounded-hosted popup that does a one-time email verification; Bounded never holds the key.

import { signAndSubmitTransaction } from '@bounded-sh/client';
import { VersionedTransaction } from '@solana/web3.js';

// Call from a user gesture (click/tap) — signing opens a popup.
async function pay(tx: VersionedTransaction) {
  const signature = await signAndSubmitTransaction(tx); // signs + submits, returns the hash
  console.log('submitted', signature);
}

signMessage and signTransaction (sign without submit) throw for embedded smart wallets — they sign and submit atomically, so use signAndSubmitTransaction.

Usage

Get Current User

import { getCurrentUser } from '@bounded-sh/client';

const user = await getCurrentUser();
if (user) {
	console.log('Current user:', user.address);
} else {
	console.log('No user is currently logged in.');
}

Set Data

import { set } from '@bounded-sh/client';

const path = 'path/to/data';
const data = { key: 'value' };

await set(path, data);

console.log(`Data set at ${path}`);

On-Chain and Off-Chain Data

The set and get functions automatically handle whether data is stored on-chain or off-chain based on your application's policy configuration in the Tarobase Console.

On-Chain Data:

  • If the data path corresponds to on-chain storage defined in your application policy, the set function will store data on-chain.

  • The data must adhere to the fields schema defined in your Tarobase policy.

  • Transactions are handled automatically, and users may be prompted to approve blockchain transactions.

  • On-chain transactions require an explicit Solana RPC URL in init({ rpcUrl }); the SDK does not choose a bundled mainnet/devnet RPC when this is omitted.

Off-Chain Data:

  • If the data path corresponds to off-chain storage, the set function will store data off-chain.

Important:

  • Manage your data storage preferences and define policies at the Tarobase Console.

  • Ensure that for on-chain data, the parameters you provide to set match the fields specified in your Tarobase policy.

  • The policy also controls what can be set or get in your application.

  • For information on how to form your policy specifically for your app's use-cases, view the policy docs here.

Subscribe

import { subscribe } from '@bounded-sh/client';

// Subscribe to a single document
const unsubscribe = await subscribe('users/123/profile', {
	onData: (data) => console.log('Data updated:', data),
	onError: (e) => console.error(e.message),
});

// Subscribe to a filtered, live collection feed
await subscribe('orders', {
	filter: { status: 'open', amount: { $gt: 100 } },
	limit: 50,
	onData: (orders) => console.log('Live open orders:', orders),
});

// Later, to stop receiving updates:
await unsubscribe();

Get Data

import { get } from '@bounded-sh/client';

// Read a single document
const message = await get('messages/abc123');

// Query a collection with a structured filter, sort, limit, and cursor pagination
const recent = await get('messages', {
	filter: { text: { $regex: '^f', $options: 'i' } }, // text starting with "f"
	sort: { tarobase_created_at: -1 },
	limit: 20,
	// cursor: previousPage.nextCursor,  // opaque keyset cursor (no numeric offset)
});

console.log('Messages:', recent);

filter uses MongoDB-style operators ($gt, $gte, $lt, $lte, $ne, $in, $nin, $all, $size, $elemMatch, $exists, $type, $regex, $and, $or, $nor) evaluated server-side. See the Querying reference for the full table.

Note: The older natural-language prompt option is legacy and no longer filters results — always use the structured filter option above.

Note: Similar to set, the get and subscribe functions will automatically retrieve data from on-chain or off-chain storage based on your application's configuration.

Counting, Aggregation, Search, and Joins

import { count, aggregate, queryAggregate, search, get } from '@bounded-sh/client';

// Count matching documents
const { value: openCount } = await count('orders', { filter: { status: 'open' } });

// Single scalar aggregate: count | uniqueCount | sum | avg | min | max
const { value: total } = await aggregate('orders', 'sum', { field: 'amount' });

// Grouped aggregation → one row per group
const byCategory = await queryAggregate('spend', { groupBy: ['category'], count: true, sum: ['amount'] });

// Full-text search (collection must declare `search: { fields: [...] }` in policy)
const docs = await search('orgs/o1/docs', 'quarterly report', { fields: ['title', 'body'] });

// Relationship join via `shape` (collection must declare `links` in policy)
const projects = await get('projects', { shape: { owner: {} } });

Get Multiple Documents

Use getMany to efficiently fetch multiple documents in a single request:

import { getMany } from '@bounded-sh/client';

// Fetch multiple documents at once (max 30 paths)
const results = await getMany([
  'users/123/profile',
  'users/456/profile',
  'posts/789'
]);

// Each result contains: { path, data, error? }
results.forEach(result => {
  if (result.error) {
    console.log(`Error for ${result.path}: ${result.error.message}`);
  } else {
    console.log(`Data for ${result.path}:`, result.data);
  }
});

The getMany function:

  • Accepts up to 30 document paths per request

  • Returns results in the same order as the input paths

  • Includes per-document error handling (NOT_FOUND, UNAUTHORIZED)

  • Automatically caches successful fetches

Additional Resources

For more comprehensive examples, detailed guides, and API references, please visit our documentation site:

React Native Support

React Native and Expo are supported through the same @bounded-sh/client package. See REACT_NATIVE.md for platform setup, hosted login, and guest login examples.

Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

License

This Bounded JS SDK project is licensed under the MIT License.


Thank you for using the Bounded JS SDK. If you have questions or need help, please create an issue.