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

@archie/js-sdk

v0.1.1

Published

Archie BAAS - Core isomorphic TypeScript SDK

Readme

@archie/js-sdk

Core TypeScript SDK for the Archie BAAS platform. Isomorphic — works in both browser and Node.js.

Table of Contents


Installation

npm install @archie/js-sdk
# or
pnpm add @archie/js-sdk

Quick Start

import { createClient } from '@archie/js-sdk';

const archie = createClient({
  projectId: 'your-project-uuid',
  apiKey: 'anon your-api-key',
  apiUrl: 'https://your-project.archiecore.com',
  environment: 'master',
});

// Sign in
const { session } = await archie.auth.signIn({
  email: '[email protected]',
  password: 'secret',
});

// Query data
const { data, error } = await archie.graphql.query('{ users { id email } }');

Configuration

const archie = createClient({
  // Required
  projectId: 'your-project-uuid',

  // API key — sent as-is in the Authorization header when no user JWT is present.
  // Examples: 'anon xxxxx', 'Bearer eyJhbG...', 'custom-prefix token123'
  apiKey: 'anon your-api-key',

  // Environment (default: 'master')
  environment: 'development',

  // Base URLs
  apiUrl: 'https://example.platform.com',
  authUrl: 'https://example.platform.com', // defaults to apiUrl
  realtimeUrl: 'https://example.platform.com', // defaults to apiUrl

  // Session behavior
  autoRefreshToken: true, // auto-refresh JWT before expiry (default: true)
  persistSession: true, // persist session in storage (default: true)

  // Custom storage adapter (default: localStorage in browser, memory in Node)
  storageAdapter: customAdapter,

  // Extra headers added to every request
  headers: { 'x-custom': 'value' },

  // Retry & timeout
  timeout: 30000, // request timeout in ms (default: 30000, 0 = disabled)
  retries: 3, // max retry attempts for transient errors (default: 0)
  retryDelay: 200, // initial backoff delay in ms (default: 200)

  // Custom fetch — for testing, proxies, or edge runtimes
  fetch: customFetchImpl,

  // Logging
  debug: false, // enable console logging (default: false)
  logger: customLogger, // custom Logger implementation (overrides debug flag)
});

| Option | Type | Default | Description | | ------------------ | ----------------------- | -------------------------- | ---------------------------------------- | | projectId | string | required | Project UUID | | apiKey | string | undefined | API key, sent as-is in Authorization | | environment | string | 'master' | Environment name | | apiUrl | string | 'https://api.archie.dev' | API Manager URL | | authUrl | string | same as apiUrl | Auth service URL | | realtimeUrl | string | same as apiUrl | WebSocket URL (auto-converts to ws://) | | autoRefreshToken | boolean | true | Auto-refresh JWT 60s before expiry | | persistSession | boolean | true | Persist session in storage | | storageAdapter | StorageAdapter | auto-detect | Custom storage (localStorage or memory) | | headers | Record<string,string> | {} | Extra headers on every request | | timeout | number | 30000 | Request timeout in ms (0 = disabled) | | retries | number | 0 | Max retry attempts for transient errors | | retryDelay | number | 200 | Initial backoff delay in ms | | fetch | typeof fetch | globalThis.fetch | Custom fetch for testing/edge runtimes | | debug | boolean | false | Enable console logging | | logger | Logger | noop | Custom logger implementation |

Authorization Priority

The SDK determines the Authorization header using this priority:

  1. User JWT (after signIn) → Authorization: Bearer {jwt}
  2. API key (from config) → Authorization: {apiKey} (sent as-is)
  3. Nothing → no Authorization header

Auth Module

Access via archie.auth. Handles registration, login, session management, auto-refresh, and auth events.

Sign Up

const { userId, message } = await archie.auth.signUp({
  email: '[email protected]',
  password: 'StrongP@ss1',
  firstName: 'Jane', // optional
  lastName: 'Doe', // optional
  roleId: 'role-uuid', // optional
});
console.log(message); // "Confirmation code sent to email"

Confirm Sign Up

After the user receives a verification code by email:

const { session } = await archie.auth.confirmSignUp({
  email: '[email protected]',
  code: '123456',
});
// User is now signed in — session contains accessToken, refreshToken, user

Sign In

const { session } = await archie.auth.signIn({
  email: '[email protected]',
  password: 'secret',
});

console.log(session.user); // { id, email, firstName, lastName, roles }
console.log(session.accessToken); // JWT
console.log(session.expiresAt); // Unix timestamp (seconds)

Sign Out

await archie.auth.signOut();
// Clears session, storage, and stops auto-refresh

Get Current Session / User

const session = archie.auth.getSession(); // Session | null
const user = archie.auth.getUser(); // User | null

if (user) {
  console.log(user.id, user.email, user.roles);
}

Wait for Initialization

On page load, the SDK restores the session from storage asynchronously. Wait for it before checking auth state:

await archie.auth.waitForInit();
const user = archie.auth.getUser(); // now guaranteed to be loaded

Auth State Changes

const unsubscribe = archie.auth.onAuthStateChange((event, session) => {
  // event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED'
  switch (event) {
    case 'SIGNED_IN':
      console.log('Logged in as', session.user.email);
      break;
    case 'SIGNED_OUT':
      console.log('Logged out');
      break;
    case 'TOKEN_REFRESHED':
      console.log('Token refreshed silently');
      break;
  }
});

// Stop listening
unsubscribe();

Password Recovery

// Step 1: Request recovery email
const { message } = await archie.auth.recoverPassword({
  email: '[email protected]',
});

// Step 2: Reset password with the code received by email
const { message } = await archie.auth.resetPassword({
  email: '[email protected]',
  code: '123456',
  newPassword: 'NewStr0ngP@ss',
});

Manual Token Refresh

Usually handled automatically when autoRefreshToken: true, but can be called manually:

const { session } = await archie.auth.refreshSession();

Get JWKS

Fetch the JSON Web Key Set for server-side JWT verification:

const { keys } = await archie.auth.getJWKS();

Auto-Refresh Behavior

When autoRefreshToken: true (default):

  • After sign in, the SDK schedules a token refresh 60 seconds before expiry
  • On refresh success, emits TOKEN_REFRESHED and schedules the next refresh
  • On refresh failure, calls signOut() automatically

Session Persistence

When persistSession: true (default):

  • Session is saved to storage after every sign in and token refresh
  • On client creation, the SDK restores the session from storage
  • If the stored token is expired, it attempts a refresh automatically
  • Storage key format: archie-auth-{projectId}-{environment}

GraphQL Module

Access via archie.graphql. Execute queries and mutations against the Archie API Manager's /graphql endpoint.

Query

Returns { data, error } — never throws for GraphQL-level errors:

interface User {
  id: string;
  email: string;
  name: string;
}

const { data, error } = await archie.graphql.query<{ users: User[] }>(
  '{ users { id email name } }',
);

if (error) {
  console.error(error.message, error.code);
} else {
  console.log(data.users);
}

Query with Variables

const { data, error } = await archie.graphql.query<{ user: User }>(
  `query GetUser($id: ID!) {
    user(id: $id) { id email name }
  }`,
  { id: 'user-123' },
);

Mutation

const { data, error } = await archie.graphql.mutate<{ createUser: User }>(
  `mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) { id email }
  }`,
  { input: { email: '[email protected]', name: 'New User' } },
);

Raw Request (Advanced)

For cases where you want the raw data and want errors to throw:

try {
  const data = await archie.graphql.request<{ users: User[] }>({
    query: '{ users { id email } }',
    variables: {},
    operationName: 'GetUsers',
  });
  console.log(data.users); // direct access, no { data, error } wrapper
} catch (err) {
  // Throws GraphQLError on failure
}

Request Options

Both query() and mutate() accept an optional third parameter:

const controller = new AbortController();

const { data } = await archie.graphql.query(
  '{ users { id } }',
  {},
  {
    headers: { 'x-custom': 'value' }, // extra headers for this request
    signal: controller.signal, // AbortSignal for cancellation
  },
);

// Cancel the request
controller.abort();

GraphQL Response Shape

// Success case
{ data: T, error: null }

// Error case (GraphQL-level errors — partial data may exist)
{ data: T | null, error: GraphQLError }

// Network/HTTP error
{ data: null, error: ArchieError }

Files Module

Access via archie.files. Upload, download, and manage files via GraphQL multipart uploads.

Upload a File

// From a File input
const input = document.querySelector<HTMLInputElement>('#fileInput');
const file = input.files[0];

const { url, fileId } = await archie.files.upload(file, {
  filename: 'photo.jpg',
  contentType: 'image/jpeg',
  providerType: 's3', // optional — storage provider hint
  onProgress: (pct) => console.log(`${pct}% uploaded`), // optional — progress callback
});

console.log(url); // CDN URL of the uploaded file
console.log(fileId); // Unique file ID for download/reference

Upload from a Blob or Uint8Array

// Blob
const blob = new Blob(['Hello, world!'], { type: 'text/plain' });
const result = await archie.files.upload(blob, { filename: 'hello.txt' });

// Uint8Array (works in Node.js too)
const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
const result = await archie.files.upload(bytes, { filename: 'hello.bin' });

Upload CSV

Import a CSV file into a database table:

const csvFile = document.querySelector<HTMLInputElement>('#csvInput').files[0];

const { result } = await archie.files.uploadCsv(csvFile, {
  tableName: 'products',
  transactionality: true, // optional — rollback all on error
  limit: 1000, // optional — max rows to import
});

console.log(result.success); // boolean
console.log(result.rowsImported); // number
console.log(result.errors); // any import-level errors

Download a File

const blob = await archie.files.download('file-id-123');

// In the browser, trigger a download:
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'my-file.pdf';
a.click();
URL.revokeObjectURL(url);

Get File URL

Builds the file URL locally, no network request:

const url = archie.files.getUrl('file-id-123');
// → "https://example.platform.com/files?id=file-id-123"

Realtime Module

Access via archie.realtime. WebSocket subscriptions using the graphql-transport-ws protocol with lazy connection and auto-reconnect.

Subscribe to a GraphQL Subscription

const sub = archie.realtime.subscribe<{ orderUpdated: Order }>(
  `subscription {
    orderUpdated { id status total }
  }`,
  {},
  {
    onData: (data) => {
      console.log('Order updated:', data.orderUpdated);
    },
    onError: (error) => {
      console.error('Subscription error:', error.message);
    },
    onComplete: () => {
      console.log('Subscription ended');
    },
  },
);

// Later: stop listening
sub.unsubscribe();

Subscribe with Variables

const sub = archie.realtime.subscribe(
  `subscription OnUserActivity($userId: ID!) {
    userActivity(userId: $userId) { action timestamp }
  }`,
  { userId: 'user-123' },
  { onData: (data) => console.log(data) },
);

Channel-Based API

A higher-level convenience API for table-level event subscriptions:

const channel = archie.realtime.channel('orders');

channel
  .on('INSERT', (payload) => {
    console.log('New order:', payload);
  })
  .on('UPDATE', (payload) => {
    console.log('Order updated:', payload);
  })
  .on('DELETE', (payload) => {
    console.log('Order deleted:', payload);
  })
  .on('*', (payload) => {
    console.log('Any event:', payload);
  });

// Start listening
channel.subscribe();

// Stop listening
channel.unsubscribe();

Connection State

// Current state
console.log(archie.realtime.currentState);
// → 'DISCONNECTED' | 'CONNECTING' | 'CONNECTED' | 'RECONNECTING'

// Listen for changes
const unsubscribe = archie.realtime.onConnectionStateChange((state) => {
  console.log('WebSocket state:', state);
  if (state === 'RECONNECTING') {
    showReconnectingBanner();
  }
});

unsubscribe();

Auto-Reconnect

The WebSocket automatically reconnects with exponential backoff when the connection drops:

  • Backoff schedule: 1s → 2s → 4s → 8s → 16s → 30s (max)
  • Reconnect counter resets after a successful connection_ack
  • The connection closes automatically when the last subscription is unsubscribed
  • On TOKEN_REFRESHED, the connection is re-established with the new credentials

REST Module

Access via archie.rest. For consuming custom REST APIs created via the Archie gateway. All standard headers (x-project-id, authorization, environment) are injected automatically.

GET

const products = await archie.rest.get<Product[]>('/api/products');

// With query parameters
const results = await archie.rest.get<Product[]>('/api/products', {
  params: { category: 'electronics', limit: '10' },
});
// → GET /api/products?category=electronics&limit=10

POST

const created = await archie.rest.post<Product>('/api/products', {
  name: 'Widget',
  price: 9.99,
});

PUT

await archie.rest.put('/api/products/123', {
  name: 'Updated Widget',
  price: 12.99,
});

PATCH

await archie.rest.patch('/api/products/123', { price: 14.99 });

DELETE

await archie.rest.delete('/api/products/123');

Request Options

All REST methods accept an options parameter:

const controller = new AbortController();

const data = await archie.rest.get('/api/products', {
  headers: { 'x-custom': 'value' },
  params: { page: '2' },
  signal: controller.signal,
});

REST Error Handling

REST methods throw ArchieError on non-2xx responses:

try {
  await archie.rest.get('/api/products/missing');
} catch (err) {
  if (err instanceof ArchieError) {
    console.log(err.status); // 404
    console.log(err.code); // 'HTTP_404'
    console.log(err.message); // server-provided message
  }
}

Error Handling

The SDK provides a structured error hierarchy. All errors extend ArchieError.

Error Classes

| Class | When | Key Properties | | -------------- | ------------------------------------------- | ---------------------------------------------- | | ArchieError | Base class for all SDK errors | message, code, status, details, hint | | AuthError | Authentication failures (401/403) | Same as ArchieError | | GraphQLError | GraphQL response errors | path, locations + ArchieError props | | NetworkError | Network issues (timeout, DNS, disconnected) | hint = "Check your internet..." |

Catching Errors

import { ArchieError, AuthError, GraphQLError, NetworkError } from '@archie/js-sdk';

try {
  await archie.auth.signIn({ email: '[email protected]', password: 'wrong' });
} catch (err) {
  if (err instanceof AuthError) {
    console.log(err.code); // 'AUTH_INVALID_CREDENTIALS'
    console.log(err.status); // 401
    console.log(err.message); // 'Invalid credentials'
  } else if (err instanceof NetworkError) {
    console.log(err.hint); // 'Check your internet connection or API URL'
  } else if (err instanceof ArchieError) {
    console.log(err.code, err.status, err.details);
  }
}

GraphQL Error Handling

query() and mutate() return errors in the response instead of throwing:

const { data, error } = await archie.graphql.query('{ invalidField }');

if (error) {
  console.log(error.message); // 'Cannot query field "invalidField"'
  console.log(error.code); // 'GRAPHQL_VALIDATION_FAILED'
  console.log(error.path); // ['invalidField']
  console.log(error.locations); // [{ line: 1, column: 3 }]
}

Auth Error Codes

| Code | Description | | -------------------------- | ------------------------------------------- | | AUTH_INVALID_CREDENTIALS | Wrong email/password | | AUTH_EMAIL_EXISTS | Email already registered | | AUTH_EMAIL_NOT_VERIFIED | Email confirmation required | | AUTH_ACCOUNT_LOCKED | Account temporarily locked | | AUTH_INVALID_CODE | Invalid verification/recovery code | | AUTH_TOKEN_EXPIRED | Access token has expired | | AUTH_TOKEN_INVALID | Malformed or revoked token | | AUTH_NOT_CONFIGURED | Auth not enabled for the project | | AUTH_WEAK_PASSWORD | Password doesn't meet strength requirements |

Automatic 401 Retry

When a request returns 401:

  1. The SDK attempts to refresh the token automatically
  2. If refresh succeeds, the original request is retried with the new token
  3. If refresh fails, the user is signed out and the error is thrown

This is transparent — your code doesn't need to handle it.


Retry & Timeout

The SDK supports automatic retries with exponential backoff and per-request timeouts.

Configuration

const archie = createClient({
  projectId: 'your-project-uuid',
  retries: 3, // retry up to 3 times on transient errors
  retryDelay: 200, // initial delay: 200ms → 400ms → 800ms (with jitter)
  timeout: 15_000, // abort requests after 15 seconds
});

Retryable Conditions

The SDK retries automatically on:

| Condition | Status Codes / Errors | | ---------------- | ------------------------------------- | | Server errors | 500, 502, 503, 504 | | Rate limiting | 429 (respects Retry-After header) | | Network failures | TypeError (DNS, connectivity) | | Timeouts | TIMEOUT errors |

Non-retryable errors (400, 401, 403, 404, 409, etc.) are thrown immediately.

Backoff Strategy

Exponential with jitter to prevent thundering herd:

delay = min(baseDelay × 2^(attempt-1) + random_jitter, 30s)

Timeout Behavior

  • When timeout > 0, each individual request (including retries) is aborted after timeout milliseconds
  • Throws ArchieError with code: 'TIMEOUT' and status: 408
  • Set timeout: 0 to disable (no timeout)
  • User-provided AbortSignal takes precedence over timeout

Custom Fetch

Inject a custom fetch implementation for testing, proxies, or edge runtimes:

import { createClient } from '@archie/js-sdk';

// Testing with a mock
const mockFetch = vi.fn().mockResolvedValue(new Response('{}'));
const archie = createClient({
  projectId: 'test',
  fetch: mockFetch,
});

// Edge runtime (Cloudflare Workers, Deno)
const archie = createClient({
  projectId: '...',
  fetch: globalThis.fetch, // or a custom implementation
});

// Proxy / logging middleware
const archie = createClient({
  projectId: '...',
  fetch: async (url, init) => {
    console.log('→', init?.method, url);
    const response = await globalThis.fetch(url, init);
    console.log('←', response.status);
    return response;
  },
});

Health Check

Verify connectivity to the API before performing operations:

const isUp = await archie.ping();

if (isUp) {
  console.log('API is reachable');
} else {
  console.warn('API is unreachable');
}

Returns true if the API responds with a 2xx status, false otherwise. Never throws.


Storage Adapters

The SDK uses a StorageAdapter interface for session persistence. You can provide a custom implementation.

Interface

interface StorageAdapter {
  getItem(key: string): string | null | Promise<string | null>;
  setItem(key: string, value: string): void | Promise<void>;
  removeItem(key: string): void | Promise<void>;
}

Built-in Adapters

import { BrowserLocalStorage, MemoryStorage } from '@archie/js-sdk';

// Browser — uses localStorage (default in browser)
const archie = createClient({
  projectId: '...',
  storageAdapter: new BrowserLocalStorage(),
});

// In-memory — for SSR, tests, or environments without localStorage
const archie = createClient({
  projectId: '...',
  storageAdapter: new MemoryStorage(),
});

Custom Adapter Example (AsyncStorage for React Native)

import AsyncStorage from '@react-native-async-storage/async-storage';

const archie = createClient({
  projectId: '...',
  storageAdapter: {
    getItem: (key) => AsyncStorage.getItem(key),
    setItem: (key, value) => AsyncStorage.setItem(key, value),
    removeItem: (key) => AsyncStorage.removeItem(key),
  },
});

TypeScript Types

All types are exported for strong typing in your application.

Core Types

import type { ArchieClientOptions, Session, User } from '@archie/js-sdk';

Auth Types

import type {
  AuthSignUpParams,
  AuthSignInParams,
  AuthConfirmParams,
  AuthRecoverParams,
  AuthResetPasswordParams,
  AuthEvent, // 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED'
  AuthEventCallback,
} from '@archie/js-sdk';

GraphQL Types

import type {
  GraphQLResponse, // { data: T | null; error: ArchieError | null }
  GraphQLRequestOptions, // { headers?, signal? }
  GraphQLRawRequest, // { query, variables?, operationName? }
} from '@archie/js-sdk';

File Types

import type {
  FileUploadOptions, // { filename?, contentType?, providerType?, onProgress? }
  FileUploadResult, // { url: string; fileId: string }
  CsvUploadOptions, // { tableName, transactionality?, limit? }
} from '@archie/js-sdk';

Realtime Types

import type {
  Subscription, // { unsubscribe: () => void }
  SubscriptionCallbacks, // { onData, onError?, onComplete? }
  RealtimeEvent, // 'INSERT' | 'UPDATE' | 'DELETE' | '*'
  RealtimeChannel, // { on, subscribe, unsubscribe }
  ConnectionState, // 'CONNECTING' | 'CONNECTED' | 'DISCONNECTED' | 'RECONNECTING'
  ConnectionStateCallback,
} from '@archie/js-sdk';

REST Types

import type {
  RestRequestOptions, // { headers?, params?, signal? }
} from '@archie/js-sdk';

Module Interfaces (for DI / Testing)

import type {
  IAuthModule,
  IGraphQLModule,
  IFileModule,
  IRealtimeModule,
  IRestModule,
  IHttpClient,
  Logger,
  TokenAccessor,
  StorageAdapter,
} from '@archie/js-sdk';

Module Classes (for advanced typing)

The concrete module implementations are also exported for instanceof checks or advanced use:

import { AuthModule, GraphQLModule, FileModule, RealtimeModule, RestModule } from '@archie/js-sdk';

Built-in Loggers

Two logger constants are available for the logger option:

import { consoleLogger, noopLogger } from '@archie/js-sdk';

// Console logger — logs to console.debug/info/warn/error
const archie = createClient({ projectId: '...', logger: consoleLogger });

// Noop logger — silences all logging (default)
const archie = createClient({ projectId: '...', logger: noopLogger });

License

MIT