
# 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](#installation)

- [Getting Started](#getting-started)

	- [Initialization](#initialization)

	- [Authentication](#authentication)

- [Usage Examples](#usage-examples)

	- [Initialize the SDK](#initialize-the-sdk)

	- [Authenticate a User](#authenticate-a-user)

	- [Get Current User](#get-current-user)

	- [Set Data](#set-data)

		- [On-Chain and Off-Chain Data](#on-chain-and-off-chain-data)

	- [Subscribe](#subscribe)

	- [Get Data](#get-data)

	- [Counting, Aggregation, Search, and Joins](#counting-aggregation-search-and-joins)

	- [Get Multiple Documents](#get-multiple-documents)

- [Additional Resources](#additional-resources)

- [React Native Support](#react-native-support)

- [Contributing](#contributing)

- [License](#license)

  

## Installation
  
Install the SDK via npm:

  
```bash
npm install @bounded-sh/client
```
  

Or with Yarn:
```bash
yarn add @bounded-sh/client
```

### `npm audit` reports a moderate `uuid` advisory

`npm audit --omit=dev` reports GHSA-w5hq-g745-h8pq through this production path:

```
@bounded-sh/client -> @solana/web3.js -> jayson -> uuid@8.3.2
```

**The vulnerable code path is not reachable.** The advisory covers `uuid`'s
v3/v5/v6 buffer-writing path when a `buf` argument is supplied; jayson only ever
calls `uuid.v4()` with no arguments, so nothing in this dependency graph can
reach it. There is no upstream fix today: `@solana/web3.js@1.98.4` is the latest
1.x, and every published `jayson` release pins `uuid: ^8.3.2`.

If your release process requires a clean production audit, add a scoped override
in **your own** `package.json` (npm honors `overrides` only in the root project,
so the SDK cannot apply this for you):

```json
  "overrides": { "jayson": { "uuid": "^11.1.1" } }
```

Yarn users use the equivalent `resolutions` entry. **Remove the override
manually** once jayson ships a fixed `uuid` - it does not retire itself.

  

## Getting Started
### Initialization

  

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

  
```typescript
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.

```typescript
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:
  
```typescript
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.

```typescript
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`.

### Platform app and build control

The headless `apps` and `builds` namespaces provision apps and control unified
build runs from registered first-party product surfaces.
Developer-api CORS blocks browser calls from generated-app origins.
Server-side callers are unaffected by that CORS boundary.

```typescript
import { apps, builds } from '@bounded-sh/client';

// Parentless genesis: the app (slug + routable URL + proven starter policy)
// exists the moment create returns. idempotencyKey is the create identity —
// retries with the same key converge on the same app.
const app = await apps.create({ prompt, idempotencyKey, intent: 'create-flow' });

const submitted = await builds.submit(app.appId, {
  prompt,
  operation: 'create',
  idempotencyKey,
});
const watcher = builds.watch(app.appId, submitted.runId, ({ run, events }) => {
  console.log(run.state, events);
});
// watcher.stop();
```

## Usage

### Get Current User


```typescript
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
```typescript
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](https://console.tarobase.com).
  

**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](https://console.tarobase.com).

- 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](https://docs.tarobase.com/policies).

  
### Subscribe

```typescript
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

```typescript
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](https://docs.tarobase.com/querying) 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

```typescript
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:

```typescript
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:

- [Tarobase Documentation](https://docs.tarobase.com)

- Quickstart Guide

- [API Reference](https://docs.tarobase.com/api)


### React Native Support
React Native and Expo are supported through the same `@bounded-sh/client`
package. See [REACT_NATIVE.md](./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.
