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

@objectstack/client

v2.0.5

Published

Official Client SDK for ObjectStack Protocol

Readme

@objectstack/client

The official TypeScript client for ObjectStack.

Features

  • Auto-Discovery: Connects to your ObjectStack server and automatically configures API endpoints.
  • Typed Metadata: Retrieve Object and View definitions with full type support.
  • Metadata Caching: ETag-based conditional requests for efficient metadata caching.
  • Unified Data Access: Simple CRUD operations for any object in your schema.
  • Batch Operations: Efficient bulk create/update/delete with transaction support.
  • View Storage: Save, load, and share custom UI view configurations.
  • Standardized Errors: Machine-readable error codes with retry guidance.
  • Full Protocol Coverage: Implements all 13 API namespaces defined in @objectstack/spec
  • 95+ Methods: Complete implementation of discovery, metadata, data, auth, workflow, realtime, AI, and more.

🤖 AI Development Context

Role: Browser/Node Client SDK Usage:

  • Use this in Frontend Apps (React, etc.) or external Node scripts.
  • Interacts with packages/runtime over HTTP.

Key namespaces:

  • client.meta: Metadata operations.
  • client.data: Data operations.

Installation

pnpm add @objectstack/client

Usage

import { ObjectStackClient } from '@objectstack/client';

// 1. Initialize
const client = new ObjectStackClient({
  baseUrl: 'http://localhost:3004', // Your ObjectStack Server URL
  token: 'optional-auth-token'
});

async function main() {
  // 2. Connect (Fetches system capabilities)
  await client.connect();

  // 3. Metadata Access
  const todoSchema = await client.meta.getObject('todo_task');
  console.log('Fields:', todoSchema.fields);
  
  // Save Metadata (New Feature)
  await client.meta.saveItem('object', 'my_custom_object', {
    label: 'My Object',
    fields: { name: { type: 'text' } }
  });

  // 4. Advanced Query
  const tasks = await client.data.find<{ subject: string; priority: number }>('todo_task', {
    select: ['subject', 'priority'], // Field Selection
    filters: ['priority', '>=', 2],  // ObjectStack Filter AST
    sort: ['-priority'],             // Sorting
    top: 10
  });
  
  // 5. Create Data
  const newTask = await client.data.create('todo_task', {
    subject: 'New Task',
    priority: 1
  });

  // 6. Batch Operations (New!)
  const batchResult = await client.data.batch('todo_task', {
    operation: 'update',
    records: [
      { id: '1', data: { status: 'active' } },
      { id: '2', data: { status: 'active' } }
    ],
    options: {
      atomic: true,        // Rollback on any failure
      returnRecords: true  // Include full records in response
    }
  });
  console.log(`Updated ${batchResult.succeeded} records`);

  // 7. Metadata Caching (New!)
  const cachedObject = await client.meta.getCached('todo_task', {
    ifNoneMatch: '"686897696a7c876b7e"'  // ETag from previous request
  });
  if (cachedObject.notModified) {
    console.log('Using cached metadata');
  }

  // 8. View Storage (New!)
  const view = await client.views.create({
    name: 'active_tasks',
    label: 'Active Tasks',
    object: 'todo_task',
    type: 'list',
    visibility: 'public',
    query: {
      object: 'todo_task',
      where: { status: 'active' },
      orderBy: [{ field: 'priority', order: 'desc' }],
      limit: 50
    },
    layout: {
      columns: [
        { field: 'subject', label: 'Task', width: 200 },
        { field: 'priority', label: 'Priority', width: 100 }
      ]
    }
  });
}

API Reference

client.connect()

Initializes the client by fetching the system discovery manifest from /api/v1.

client.meta

  • getObject(name: string): Get object schema.
  • getCached(name: string, options?): Get object schema with ETag-based caching.
  • getView(name: string): Get view configuration.

client.data

  • find<T>(object, options): Advanced query with filtering, sorting, selection.
  • get<T>(object, id): Get single record by ID.
  • query<T>(object, ast): Execute complex query using full AST.
  • create<T>(object, data): Create record.
  • batch(object, request): Recommended - Execute batch operations (create/update/upsert/delete) with full control.
  • createMany<T>(object, data[]): Batch create records (convenience method).
  • update<T>(object, id, data): Update record.
  • updateMany<T>(object, records[], options?): Batch update records (convenience method).
  • delete(object, id): Delete record.
  • deleteMany(object, ids[], options?): Batch delete records (convenience method).

client.views (New!)

  • create(request): Create a new saved view.
  • get(id): Get a saved view by ID.
  • list(request?): List saved views with optional filters.
  • update(request): Update an existing view.
  • delete(id): Delete a saved view.
  • share(id, userIds[]): Share a view with users/teams.
  • setDefault(id, object): Set a view as default for an object.

Query Options

The find method accepts an options object:

  • select: Array of field names to retrieve.
  • filters: Simple key-value map OR Filter AST ['field', 'op', 'value'].
  • sort: Sort string ('name') or array ['-created_at', 'name'].
  • top: Limit number of records.
  • skip: Offset for pagination.

Batch Options

Batch operations support the following options:

  • atomic: If true, rollback entire batch on any failure (default: true).
  • returnRecords: If true, return full record data in response (default: false).
  • continueOnError: If true (and atomic=false), continue processing remaining records after errors.
  • validateOnly: If true, validate records without persisting changes (dry-run mode).

Error Handling

The client provides standardized error handling with machine-readable error codes:

try {
  await client.data.create('todo_task', { subject: '' });
} catch (error) {
  console.error('Error code:', error.code);        // e.g., 'validation_error'
  console.error('Category:', error.category);      // e.g., 'validation'
  console.error('HTTP status:', error.httpStatus); // e.g., 400
  console.error('Retryable:', error.retryable);    // e.g., false
  console.error('Details:', error.details);        // Additional error info
}

Error Code Reference

Client Errors (4xx)

| Error Code | HTTP Status | Retryable | Description | |------------|-------------|-----------|-------------| | validation_error | 400 | No | Input validation failed. Check error.details for field-specific errors | | unauthenticated | 401 | No | Authentication required. Provide valid token | | permission_denied | 403 | No | Insufficient permissions for this operation | | resource_not_found | 404 | No | Resource does not exist. Verify object name or record ID | | conflict | 409 | No | Resource conflict (e.g., duplicate unique field) | | rate_limit_exceeded | 429 | Yes | Too many requests. Wait before retrying |

Server Errors (5xx)

| Error Code | HTTP Status | Retryable | Description | |------------|-------------|-----------|-------------| | internal_error | 500 | Yes | Server encountered an error. Retry with backoff | | service_unavailable | 503 | Yes | Service temporarily unavailable. Retry later | | gateway_timeout | 504 | Yes | Request timeout. Consider increasing timeout or retrying |

Retry Strategy Example:

async function retryableRequest<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error: any) {
      if (!error.retryable || i === maxRetries - 1) {
        throw error;
      }
      // Exponential backoff
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
    }
  }
  throw new Error('Max retries exceeded');
}

// Usage
const data = await retryableRequest(() => 
  client.data.create('todo_task', { subject: 'Task' })
);

Protocol Compliance

The @objectstack/client SDK implements all 13 API namespaces defined in the @objectstack/spec protocol specification:

| Namespace | Purpose | Status | |-----------|---------|:------:| | discovery | API version & capabilities detection | ✅ | | meta | Metadata operations (objects, plugins, etc.) | ✅ | | data | CRUD & query operations | ✅ | | auth | Authentication & user management | ✅ | | packages | Plugin/package lifecycle management | ✅ | | views | UI view definitions | ✅ | | workflow | Workflow state transitions | ✅ | | analytics | Analytics queries | ✅ | | automation | Automation triggers | ✅ | | i18n | Internationalization | ✅ | | notifications | Push notifications | ✅ | | realtime | WebSocket subscriptions | ✅ | | ai | AI services (NLQ, chat, insights) | ✅ |

For detailed compliance verification, see CLIENT_SPEC_COMPLIANCE.md.

Available Namespaces

Complete API Coverage

const client = new ObjectStackClient({ baseUrl: 'http://localhost:3000' });
await client.connect();

// Discovery & Metadata
await client.meta.getTypes();                    // List metadata types
await client.meta.getItems('object');            // List all objects
await client.meta.getItem('object', 'contact');  // Get specific object

// Data Operations
await client.data.find('contact', { filters: { status: 'active' } });
await client.data.create('contact', { name: 'John' });
await client.data.update('contact', id, { status: 'inactive' });
await client.data.delete('contact', id);
await client.data.batch('contact', batchRequest);

// Authentication
await client.auth.login({ email: '[email protected]', password: 'pass' });
await client.auth.register({ email: '[email protected]', password: 'pass' });
await client.auth.me();
await client.auth.logout();
await client.auth.refreshToken('refresh-token-string');

// Package Management
await client.packages.list();
await client.packages.install({
  name: 'vendor_plugin',
  label: 'Vendor Plugin',
  version: '1.0.0',
});
await client.packages.enable('plugin-id');

// Permissions
await client.permissions.check({ object: 'contact', action: 'create' });
await client.permissions.getObjectPermissions('contact');
await client.permissions.getEffectivePermissions();

// Workflow
await client.workflow.getConfig('approval');
await client.workflow.getState('approval', recordId);
await client.workflow.transition({ object: 'approval', recordId, transition: 'submit' });
await client.workflow.approve({ object: 'approval', recordId });
await client.workflow.reject({ object: 'approval', recordId, reason: 'Incomplete' });

// Realtime
await client.realtime.connect({ protocol: 'websocket' });
await client.realtime.subscribe({ channel: 'contact', event: 'update' });
await client.realtime.setPresence({ status: 'online' });

// Notifications
await client.notifications.registerDevice({ token: 'device-token', platform: 'ios' });
await client.notifications.list({ unreadOnly: true });
await client.notifications.markRead(['notif-1', 'notif-2']);

// AI Services
await client.ai.nlq({ query: 'Show me all active contacts' });
await client.ai.chat({ message: 'Summarize this project', context: projectId });
await client.ai.suggest({ object: 'contact', field: 'industry' });
await client.ai.insights({ object: 'sales', recordId: dealId });

// Internationalization
await client.i18n.getLocales();
await client.i18n.getTranslations('zh-CN');
await client.i18n.getFieldLabels('contact', 'zh-CN');

// Analytics
await client.analytics.query({ object: 'sales', aggregations: ['sum:amount'] });
await client.analytics.meta('sales');

// Automation
await client.automation.trigger('send_welcome_email', { userId });

// File Storage
await client.storage.upload(fileData, 'user');
await client.storage.getDownloadUrl('file-123');

Testing

Unit Tests

pnpm test

Integration Tests

Note: Integration tests require a running ObjectStack server. The server is provided by a separate repository and must be set up independently.

# Start test server (in the ObjectStack server repository)
# Follow that project's documentation for test server setup
# Example: cd /path/to/objectstack-server && pnpm dev:test

# Run integration tests (in this repository)
cd packages/client
pnpm test:integration

See CLIENT_SERVER_INTEGRATION_TESTS.md for detailed test specifications.