npm package: @meshbase/sdk
Description: TypeScript SDK for the Meshbase API
Latest version: 0.1.3

# @meshbase/sdk

TypeScript SDK for Meshbase with typed access to data domains, management APIs,
webhooks, and natural-language queries.

## Installation

```bash
npm install @meshbase/sdk
# or
pnpm add @meshbase/sdk
```

## Quick Start

```typescript
import { Meshbase } from '@meshbase/sdk';

// Option A: Constructor
const mb = new Meshbase({
  apiUrl: 'https://api.meshbase.ai',
  apiKey: 'mb_wk_...',
});

// Option B: Connection string
const mb = Meshbase.connect('meshbase://mb_wk_...@api.meshbase.ai');

const products = await mb.catalog.products.nodes({ first: 10, filter: { search: 'outdoor' } });
const order = await mb.commerce.orders.get('ord_abc123');
const answer = await mb.ask('How many orders this month?');
```

## Connection Strings

Create a client from a single URL — convenient for env vars and sharing:

```typescript
// Format: meshbase://<apiKey>@<host>[:<port>][/<orgSlug>]
const mb = Meshbase.connect('meshbase://mb_wk_abc123@api.meshbase.ai');
const mb = Meshbase.connect('meshbase://mb_sk_xyz@localhost:4000');
const mb = Meshbase.connect('meshbase://mb_wk_abc@api.meshbase.ai/acme-corp');
```

Localhost/127.0.0.1 automatically uses `http://`, all other hosts use `https://`.
Use `https://api.meshbase.ai` for hosted Meshbase and `http://localhost:4000`
for local development. When extracted subgraphs are enabled, this URL should
still point to Apollo Router rather than a single subgraph host.

```bash
# Single env var instead of MESHBASE_API_URL + MESHBASE_API_KEY
MESHBASE_URL=meshbase://mb_wk_abc123@api.meshbase.ai
```

Management namespaces such as `mb.api.*`, `mb.my.*`, `mb.settings.*`,
`mb.marketplace.*`, and `mb.workspaces.*` derive their REST base URL from
`apiUrl` automatically. Only add `?managementApi=<host>[:<port>]` when you need
to override that routing in split deployments or local debugging.

## Cross-Tenant Data Access

Access subscribed provider data without manually managing subscription keys:

```typescript
const mb = new Meshbase({ apiUrl: 'https://api.meshbase.ai', apiKey: 'mb_wk_...' });

// By provider slug — resolves subscription automatically
const acme = await mb.useProvider('acme-corp');
const products = await acme.catalog.products.nodes({ first: 10 });

// By subscription ID
const client = await mb.useSubscription('sub_abc123');
const orders = await client.commerce.orders.list({ first: 20 });

// Subscribe and immediately start querying
const { subscription, client: dataCo } = await mb.subscribeAndConnect(planId);
if (dataCo) {
  const items = await dataCo.catalog.products.nodes({ first: 5 });
}
```

Under the hood, `useProvider()`/`useSubscription()` create or rotate a managed subscription key and return a scoped client. Keys are cached in memory for the session lifetime. Requires a workspace key (`mb_wk_`).

`subscribeAndConnect()` returns `{ client: null }` when the plan requires approval (`PENDING_APPROVAL`).

## Domain Modules

The SDK exposes business-data domains plus management namespaces. Collection-style
modules generally follow a consistent pattern with `list`, `get`, `create`,
`update` (where applicable), plus `listAll()` (async generator) and `nodes()`
(flat array) for paginated entities.

### Core

| Module | Access | Key Methods |
|--------|--------|-------------|
| Catalog | `mb.catalog` | products.list/get/create/update/archive, categories.list/get, brands.list |
| Commerce | `mb.commerce` | orders.list/get/create/updateStatus, cart.addItem/checkout |
| Inventory | `mb.inventory` | warehouses.list/get, stock.list(productId)/adjust/set |
| Identity | `mb.identity` | customers.list/get/create/update/archive |
| Invoice | `mb.invoice` | invoices.list/get/create/pay/void |
| Payment | `mb.payment` | payments.list/get/create |
| Subscription | `mb.subscription` | subscriptions.list/get/create/cancel/pause/resume, plans.list/get |

### Operations

| Module | Access | Key Methods |
|--------|--------|-------------|
| Booking | `mb.booking` | appointments.list/get/create/update/cancel, resources.list/get/create, resourceTypes.list |
| Project | `mb.project` | projects.list/get/create/update |
| Procurement | `mb.procurement` | purchaseOrders.list/get/create/update, goodsReceipts.list/get/create |
| Quote | `mb.quote` | quotes.list/get/create/update/accept/reject |
| Contract | `mb.contract` | contracts.list/get/create/update |
| Logistics | `mb.logistics` | shipments.list/get/create/updateStatus, drivers.list/get |
| Stock Movement | `mb.stockMovement` | movements.list/get/create |
| Time Tracking | `mb.timeTracking` | timeEntries.list/get/create/update/delete |
| Approval | `mb.approval` | requests.list/get/create, steps.approve/reject |
| Document | `mb.document` | documents.list/get/create/delete |
| Event | `mb.event` | events.list/get/create/update, registrations.list/create/cancel |
| Notification | `mb.notification` | notifications.list/unreadCount |
| Guide | `mb.guide` | guides.list/get/create/update |
| Service | `mb.service` | services.list/get/create/update |
| Webhook | `mb.webhook` | webhooks.list/get/create/update/delete/test/regenerateSecret, deliveries, availableEvents |

### Manufacturing

| Module | Access | Key Methods |
|--------|--------|-------------|
| MRP | `mb.mrp` | workOrders.list/get/create/update, boms.list/get/create |
| Asset | `mb.asset` | assets.list/get/create/update |
| Charging | `mb.charging` | stations.list/get/create/update, sessions.list/get/start/stop |

### Industry

| Module | Access | Key Methods |
|--------|--------|-------------|
| Agriculture | `mb.agriculture` | fields.list/get/create/update/archive, crops.list/get/create, cropCycles.list/get/create/update, harvests.list/get/create/update, soilAnalyses.list/get/create/update, applicationRecords.list/get/create/update, livestock.list/get/create/update/archive, certifications.list/get/create/update/archive, weather.list/record |
| Education | `mb.education` | courses.list/get/create/update, students.list/get/create, enrollments.create/update |
| Healthcare | `mb.healthcare` | patients.list/get/create/update, encounters.list/get/create |
| Hospitality | `mb.hospitality` | guestProfiles.get/create/update, menus.list/get/create/update/archive, menuItems.add/update/remove, housekeepingTasks.list/create/update, channelPartners.list/get/create/update/archive |
| Real Estate | `mb.realEstate` | properties.list/get/create/update, leases.list/get/create |

### Account & Tax

| Module | Access | Key Methods |
|--------|--------|-------------|
| Tax | `mb.tax` | rules.list/get/create/update |

### Management: Three Namespaces

Management operations use perspective-based namespaces:

| Namespace | Perspective | Access | Key Methods |
|-----------|-----------|--------|-------------|
| **API** (Provider) | "Your published API" | `mb.api` | plans.list/get/create/update/archive/activate, subscriptions.list/get/create/approve/reject/cancel/suspend, issuedKeys.issue/revoke/regenerate, accessRules.upsert/delete, securityRules.create/update/delete/events, subscriberAccess.allowlist/block, profile.publish/update/completeness |
| **My** (Subscriber) | "My stuff" | `mb.my` | apiKeys.list/create/revoke/rotate, workspaceKeys.list/create/revoke, subscriptions.list/get/usageStats |
| **Marketplace** (Public) | "Discover APIs" | `mb.marketplace` | providers.list, plans.list, subscribe |

**Cross-tenant convenience** (on the `mb` instance directly):

| Method | Description |
|--------|-------------|
| `mb.useProvider(slug)` | Returns data client for a subscribed provider |
| `mb.useSubscription(id)` | Returns data client for a subscription |
| `mb.subscribeAndConnect(planId)` | Subscribe + auto key + data client |
| `Meshbase.connect(url)` | Static factory from connection string |

### Workspace API

| Module | Access | Key Methods |
|--------|--------|-------------|
| NLQ | `mb.nlq` | query(question), stream(question) |
| Reporting | `mb.reporting` | usage.summary/timeSeries/byPlan/errors/topOperations/bySubscriber, intent.summary/timeSeries/top/funnel/bySubscriber, demand.byProduct/byService/byAsset/score/lost, activity.list |
| Compliance | `mb.compliance` | complianceRecords.list/get, consentRecords.revoke, retentionSettings.update, deletionRequests.process |
| Data Factory | `mb.dataFactory` | integrations.list/create/update/archive, runs.approve/reject, mappingTemplates.create/archive, discovery.generateUrl |
| Settings | `mb.settings` | org.updateName/updateSlug, customDomain.save/verify/remove, roles.list/get/create/update/delete/addPermissions/removePermissions/assign/availablePermissions |
| Notification | `mb.notification` | notifications.markAsRead/archive, preferences.updateOrgDefault, webhooks.create/delete |

## Pagination

### Manual cursor

```typescript
const page = await mb.catalog.products.list({ first: 20 });
for (const edge of page.edges) console.log(edge.node.name);
if (page.pageInfo.hasNextPage) {
  const next = await mb.catalog.products.list({ first: 20, after: page.pageInfo.endCursor });
}
```

### Auto-paginate with `listAll()`

```typescript
for await (const product of mb.catalog.products.listAll({ first: 100 })) {
  console.log(product.id, product.name);
}
```

### Convenience `nodes()`

Returns a flat array from a single page:

```typescript
const products = await mb.catalog.products.nodes({ first: 50, filter: { search: 'widget' } });
```

## Custom Field Selection

Every method accepts an optional `fields` parameter to override the default GraphQL selection set.

```typescript
const products = await mb.catalog.products.nodes({ first: 50, fields: 'id name' });
const order = await mb.commerce.orders.get('ord_abc123', {
  fields: 'id orderNumber status items(limit: 10) { id quantity product { id name } }',
});
```

## Debug Mode & Logging

Enable request/response logging by passing `debug: true` or a custom logger:

```typescript
const mb = new Meshbase({
  apiUrl: 'https://api.meshbase.ai',
  apiKey: 'mb_wk_...',
  debug: true, // logs to console
});

// Or bring your own structured logger (pino, winston, etc.)
const mb = new Meshbase({
  apiUrl: 'https://api.meshbase.ai',
  apiKey: 'mb_wk_...',
  debug: {
    debug: (msg, data) => myLogger.debug(msg, data),
    info: (msg, data) => myLogger.info(msg, data),
    warn: (msg, data) => myLogger.warn(msg, data),
    error: (msg, data) => myLogger.error(msg, data),
  },
});
```

## Request/Response Interceptors

Add middleware-style hooks for observability, auth token refresh, or request enrichment:

```typescript
const mb = new Meshbase({
  apiUrl: 'https://api.meshbase.ai',
  apiKey: 'mb_wk_...',
  onRequest: [
    (ctx) => {
      ctx.headers['x-request-id'] = crypto.randomUUID();
      return ctx;
    },
  ],
  onResponse: [
    (ctx) => {
      metrics.histogram('api_latency', ctx.durationMs);
      return ctx;
    },
  ],
});
```

## Natural Language Queries

Ask questions about your data in plain language. The API generates and executes a **read-only** GraphQL query for you. Requires the `nlq:use` permission.

```typescript
const result = await mb.ask('What are the top 5 products by order volume this month?');
console.log(result.explanation);
console.log(result.data);
console.log(result.query);
console.log(result.variables); // Variables used with the generated query
```

## Raw GraphQL

For queries not covered by the domain modules:

```typescript
const data = await mb.graphql<{ products: { edges: { node: { id: string; name: string } }[] } }>(
  `query($filter: ProductFilter) { products(first: 5, filter: $filter) { edges { node { id name } } } }`,
  { filter: { search: 'outdoor' } },
);
```

## Error Handling

The SDK maps GraphQL error codes to typed error classes. Error `details`, `hint`, and `code` are preserved from API responses:

```typescript
import { Meshbase, NotFoundError, RateLimitedError, MeshbaseError } from '@meshbase/sdk';

try {
  const product = await mb.catalog.products.get('prod_nonexistent');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log(error.hint);    // "Check the entity ID format (UUIDv7)..."
    console.log(error.details); // { entityType: "Product", entityId: "..." }
  }
  if (error instanceof RateLimitedError) {
    console.log(error.hint);    // "Check X-RateLimit-Remaining and Retry-After..."
  }
  if (error instanceof MeshbaseError) {
    console.log(error.code, error.message, error.hint);
  }
}
```

| Class | GraphQL Code |
|-------|--------------|
| `NotFoundError` | `NOT_FOUND` |
| `ForbiddenError` | `FORBIDDEN` |
| `BadInputError` | `BAD_USER_INPUT` |
| `UnauthenticatedError` | `UNAUTHENTICATED` |
| `ConflictError` | `CONFLICT` |
| `RateLimitedError` | `RATE_LIMITED` |

The client automatically retries on `RATE_LIMITED`, `TIMEOUT`, and `INTERNAL_SERVER_ERROR` with exponential backoff (configurable via `maxRetries`).

## Webhook Verification

Two functions available — synchronous (Node.js) and async (universal):

### Node.js (Express)

```typescript
import { verifyWebhook } from '@meshbase/sdk/webhooks';

app.post('/webhooks/meshbase', (req, res) => {
  try {
    const event = verifyWebhook(req.body, req.headers['x-webhook-signature'], 'whsec_...');
    console.log(event.type, event.organizationId, event.data);
    res.sendStatus(200);
  } catch {
    res.sendStatus(401);
  }
});
```

### Edge Runtime / Cloudflare Workers / Browser

```typescript
import { verifyWebhookAsync } from '@meshbase/sdk/webhooks';

export async function POST(req: Request) {
  const body = await req.text();
  try {
    const event = await verifyWebhookAsync(body, req.headers.get('x-webhook-signature')!, 'whsec_...');
    console.log(event.type, event.data);
    return new Response('OK', { status: 200 });
  } catch {
    return new Response('Unauthorized', { status: 401 });
  }
}
```

## Configuration

```typescript
interface MeshbaseConfig {
  apiUrl: string;                    // GraphQL API endpoint URL
  managementApiUrl?: string;          // Optional management API override
  apiKey: string;                          // API key (mb_wk_... or Bearer token)
  orgSlug?: string;                        // Organization slug for multi-tenant access
  timeoutMs?: number;                      // Request timeout in ms (default: 30000)
  maxRetries?: number;                     // Retry count for transient errors (default: 2)
  headers?: Record<string, string>;        // Extra HTTP headers
  debug?: boolean | Logger;                // Enable debug logging
  onRequest?: RequestInterceptor[];        // Pre-request middleware
  onResponse?: ResponseInterceptor[];      // Post-response middleware
}
```

`apiUrl` is enough for both data modules such as `mb.catalog`, `mb.commerce`,
or `mb.inventory`, and management modules backed by the REST surface. Use
`managementApiUrl` only as an explicit override for split deployments.

The SDK remains federation-transparent as long as `apiUrl` points at the Apollo Router entry point rather than an individual host.

Internally the config key remains `apiUrl` for backward compatibility, but in
hosted Meshbase you can treat it as the single public API edge entry point.