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

@likha-erp/likha-sdk

v1.0.7

Published

A JavaScript and TypeScript library that simplifies working with Likha ERP

Readme


title: Likha ERP SDK description: A JavaScript and TypeScript library that simplifies working with Likha ERP.

The Likha ERP SDK allows to work with Likha ERP directly in your JavaScript and TypeScript projects. The SDK is split into separate modules, giving granular control over which features to include and which can be pruned at build-time. It is lightweight and dependency-free.

npm install @likha-erp/likha-sdk

Create a Client

The Likha ERP SDK is a "Composable Client" that allows you to customize and build a client with the specific features you need. The client starts as an empty wrapper without any functionality. To add features, import and use the following composables:

| Composable | Description | | :--- | :--- | | rest() | Adds .request() method for making REST requests. | | graphql() | Adds .query() method for making GraphQL requests. | | authentication() | Adds .login(), .logout(), and .refresh() methods. Also adds token handling. | | realtime() | Adds .connect(), .subscribe(), .sendMessage(), and .onWebSocket() methods. Also adds reconnect logic. | | staticToken() | Adds .setToken() and .getToken() methods for manually managing tokens. |

JavaScript

import { createLikha, rest } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(rest());

TypeScript

You must provide a Schema when creating a Likha ERP client to make use of type hinting and completion. This schema contains definitions for each collection and provides you with type hints (on input) and completion (on output).

import { createLikha, rest } from '@likha-erp/likha-sdk';

interface Post {
  id: number;
  title: string;
  content: string;
}

interface Schema {
  posts: Post[];
}

const likha = createLikha<Schema>('http://api.likha.example.com').with(rest());

Tip: Learn how to create a Schema for SDK client creation in the Advanced Types tutorial.


Browser Usage (Static Pages)

The SDK can be used directly in static HTML pages via a <script> tag. The IIFE bundle exposes a global Likha object and includes all dependencies.

CDN URLs

After publishing to npm, the package is available on these CDNs:

| CDN | URL | |-----|-----| | unpkg | https://unpkg.com/@likha-erp/likha-sdk/dist/index.global.js | | jsDelivr | https://cdn.jsdelivr.net/npm/@likha-erp/likha-sdk/dist/index.global.js | | cdnjs (if added) | — |

These URLs always point to the latest version. For a specific version:

<!-- Specific version -->
<script src="https://unpkg.com/@likha-erp/[email protected]/dist/index.global.js"></script>

Basic Example: Fetch Items

<!DOCTYPE html>
<html>
<head>
  <title>Likha SDK Demo</title>
  <!-- Load SDK from CDN -->
  <script src="https://unpkg.com/@likha-erp/likha-sdk/dist/index.global.js"></script>
</head>
<body>
  <div id="app">
    <h1>Posts</h1>
    <ul id="posts-list"></ul>
  </div>

  <script>
    // 1. Wait for SDK to load (it's global)
    // The IIFE executes immediately, so Likha is available right away

    // 2. Create a client
    const client = Likha.createLikha('https://api.likha.example.com')
      .with(Likha.rest());

    // 3. Fetch and display posts
    client.request(Likha.readItems('posts', {
      limit: 10,
      sort: ['-date_created']
    }))
      .then(posts => {
        const list = document.getElementById('posts-list');
        posts.forEach(post => {
          const li = document.createElement('li');
          li.textContent = post.title;
          list.appendChild(li);
        });
      })
      .catch(error => {
        console.error('Failed to fetch posts:', error);
      });
  </script>
</body>
</html>

Example: Create a New Item

<script src="https://unpkg.com/@likha-erp/likha-sdk/dist/index.global.js"></script>
<script>
  const client = Likha.createLikha('https://api.likha.example.com')
    .with(Likha.rest());

  // Create a new post
  client.request(Likha.createItem('posts', {
    title: 'My New Post',
    content: 'Hello world!',
    status: 'published'
  }))
    .then(post => {
      console.log('Created post ID:', post.id);
    });
</script>

Example: Update an Item

<script src="https://unpkg.com/@likha-erp/likha-sdk/dist/index.global.js"></script>
<script>
  const client = Likha.createLikha('https://api.likha.example.com')
    .with(Likha.rest());

  // Update post with ID 5
  client.request(Likha.updateItem('posts', 5, {
    status: 'draft'
  }))
    .then(updated => console.log('Updated:', updated));
</script>

Example: Delete an Item

<script src="https://unpkg.com/@likha-erp/likha-sdk/dist/index.global.js"></script>
<script>
  const client = Likha.createLikha('https://api.likha.example.com')
    .with(Likha.rest());

  // Delete post with ID 5
  client.request(Likha.deleteItem('posts', 5))
    .then(() => console.log('Deleted'));
</script>

Accessing Available Exports

The global Likha object contains all exports:

| Global Property | Description | |-----------------|-------------| | Likha.createLikha(url) | Create a new client | | Likha.rest() | Add REST capability | | Likha.graphql() | Add GraphQL capability | | Likha.authentication() | Add auth methods | | Likha.realtime() | Add WebSocket realtime | | Likha.staticToken(token) | Set static token | | Likha.readItems(collection, options?) | Fetch items | | Likha.createItem(collection, data) | Create item | | Likha.updateItem(collection, id, data) | Update item | | Likha.deleteItem(collection, id) | Delete item | | Likha.isLikhaError(error) | Check if error is from API |


Self-Hosting (No CDN)

Download dist/index.global.js and serve it yourself:

<script src="/assets/js/likha-sdk.js"></script>

Or host on your own S3/object storage bucket and reference the absolute URL.


Error Handling in Browser

<script src="https://unpkg.com/@likha-erp/likha-sdk/dist/index.global.js"></script>
<script>
  const client = Likha.createLikha('https://api.likha.example.com')
    .with(Likha.rest());

  client.request(Likha.readItems('posts'))
    .then(data => console.log(data))
    .catch(error => {
      if (Likha.isLikhaError(error)) {
        // API returned an error (4xx, 5xx)
        console.error('API Error:', error.errors);
      } else {
        // Network error, CORS, or other issue
        console.error('Request failed:', error.message);
      }
    });
</script>

Important Notes for Browser Usage

  • Bundle size ≈ 70 KB (includes all dependencies). Minified by esbuild.
  • No module loader needed — IIFE works with plain <script> tags.
  • Modern browsers — Requires browsers with native fetch, WebSocket, URL (all browsers since ~2018). For IE11 or older, include polyfills.
  • CORS — Your Likha ERP API must allow cross-origin requests from your page's domain, or host both frontend and API on same origin.
  • Global variable — The SDK attaches to window.Likha. Avoid naming conflicts.

Making Requests

To make a request, you must create the client with the rest() or graphql() composable. If using rest(), you must also import and use one of the query functions.

import { createLikha, rest, readItems } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(rest());

const allPosts = await likha.request(readItems('posts'));

const somePosts = await likha.request(
  readItems('posts', {
    filter: { status: { _eq: 'published' } },
    sort: ['-date_created'],
    fields: ['id', 'title', 'date_created'],
    limit: 3
  })
);

Info: Breakdown of snippet

  • Imports
    • createLikha is required to create a client.
    • rest is required to make REST requests, and adds the .request() method.
    • readItems is a query function which fetches.
  • Creating the client
    • A new client is created and held in the likha variable.
    • createLikha requires the valid URL of a Likha ERP project.
    • The client is created with the rest() composable.
  • Requests
    • allPosts makes a request to readItems in the posts collection.
    • somePosts does the same, but only the specified fields from the latest 3 published items.

Note: The API Reference contains SDK examples for endpoints, showing the required function usage. View API Reference

Note: See all query parameters with SDK examples. View Query Parameters Guide

Custom Endpoints

To call custom endpoints using the SDK, you can manually provide a path and method. If using TypeScript, you can type the output.

JavaScript

import { createLikha, rest } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(rest());

const result = await likha.request(() => ({
  path: '/custom/endpoint',
  method: 'GET',
}));

TypeScript

import { createLikha, rest, customEndpoint } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(rest());

const result = await likha.request(customEndpoint<OutputType>({
  path: '/custom/endpoint',
  method: 'GET',
}));

GraphQL Usage

Add the graphql() composable to the client, and use the .query() method.

import { createLikha, graphql } from '@likha-erp/likha-sdk';

interface Post {
  id: number;
  title: string;
  content: string;
}

interface Schema {
  posts: Post[];
}

const likha = createLikha<Schema>('http://api.likha.example.com').with(graphql());

const result = await likha.query<Post[]>(`
  query {
    posts {
      id
      title
      content
    }
  }
`);

Authentication

The authentication() composable provides the Likha ERP client with new login(), logout(), and refresh() methods. It also manages token storage and refreshing on your behalf.

import { createLikha, authentication } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(authentication());

await likha.login({ email, password }, login_options);
await likha.refresh();
await likha.logout();

This approach will handle refreshing of the token automatically. The current token is stored inside the initialized client.

Login Options

The login options object contains three optional parameters to control the behavior of the request.

type LoginOptions = {
  otp?: string;
  mode?: AuthenticationMode;
  provider?: string;
};
  • otp contains the user's one-time-password if two-factor authentication is enabled.

  • mode defines how the refresh token is returned. One of json, cookie or session. Defaults to cookie.

  • provider specifies a non-redirect authentication provider. LDAP providers use identifier (typically a username) rather than email:

await client.login({ identifier: 'username', password: 'd1r3ctu5' }, { provider: 'ldap' });

For SSO providers that use browser redirects (Google, GitHub, Okta, etc.), see SSO Login below.

SSO Login

SSO providers like Google, GitHub, and Okta rely on browser redirects. The .login() method does not apply to these providers. Instead, redirect the user to Likha ERP and call .refresh() after they return.

  1. Create a link that sends the user to your Likha ERP /auth/login/<provider> endpoint with a redirect query parameter pointing back to your app. Replace <provider> with the provider name configured in AUTH_PROVIDERS.

  2. The user authenticates with the external provider.

  3. Likha ERP redirects the user back to the URL in the redirect parameter.

  4. On your callback page, call client.refresh() to establish the session.

Use session mode with credentials: 'include' so the session cookie set by Likha ERP during the redirect is sent with subsequent requests.

<a href="https://api.likha.example.com/auth/login/google?redirect=https://your-app.com/callback">
  Login with Google
</a>
import { createLikha, authentication, rest, readMe } from '@likha-erp/likha-sdk';

const client = createLikha('https://api.likha.example.com')
  .with(authentication('session', { credentials: 'include' }))
  .with(rest({ credentials: 'include' }));

await client.refresh();

const me = await client.request(readMe());

Warning: The redirect URL must be included in AUTH_<PROVIDER>_REDIRECT_ALLOW_LIST or the redirect will be blocked. See Authentication & SSO configuration for all provider settings.

Note: Read the Seamless SSO guide for session cookie configuration and local development setup.

Token Management

Set Token

Create client with token: Import staticToken and use it when creating a client.

import { createLikha, staticToken, rest } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com')
  .with(staticToken('TOKEN'))
  .with(rest());

Token for single requests: Import withToken and use it as a request function with your token as the first parameter, and your original request as the second.

import { createLikha, rest, withToken, readItems } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(rest());

const request = await likha.request(
  withToken('TOKEN', readItems('posts'))
);

Set client token manually: Import authentication or staticToken and use it when creating a client. You now have access to the setToken method.

import { createLikha, authentication } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(authentication());

await likha.setToken('TOKEN');

Get a Token

Import authentication or staticToken and use it when creating a client. You now have access to the getToken method.

import { createLikha, authentication } from '@likha-erp/likha-sdk';
const likha = createLikha('http://api.likha.example.com').with(authentication());

const token = await likha.getToken();

Configure Custom Storage

Internally, getToken() and setToken() make use of the configurable storage, which can be customized for your environment's needs. There must be a get() and set() method exposed, and the AuthData type returned.

Example Instead of storing AuthData in an object in the browser, this custom storage implementation stores and retrieves data in localStorage:

import { createLikha, authentication } from '@likha-erp/likha-sdk';

class LocalStorage {
  get() {
    return JSON.parse(localStorage.getItem("likha-data"));
  }
  set(data) {
    localStorage.setItem("likha-data", JSON.stringify(data));
  }
}

const storage = new LocalStorage();
const likha = createLikha('http://api.likha.example.com')
    .with(authentication('json', { storage }));

// Set a long term or static token without expiry information.
likha.setToken('TOKEN');

// Set custom credentials to the storage.
storage.set({
  access_token: 'token',
  refresh_token: 'token',
  expires_at: 123456789
});

Cross-Domain Cookies

A common situation is for the Likha ERP backend and frontend to be hosted on different domains, requiring extra configuration to make sure cookies are passed correctly. Usually this is only required for authentication with cookies but this can be set globally for each composable that does requests. This will then apply to all requests made using that composable:

const likha = createLikha('http://api.likha.example.com')
  .with(authentication('cookie', { credentials: 'include' }))
  .with(graphql({ credentials: 'include' }))
  .with(rest({ credentials: 'include' }));

Or you can enable this only for specific REST requests using the withOptions:

const result = await likha.request(
  withOptions(refresh(), { credentials: 'include' })
);

Realtime

The Likha ERP SDK makes it easier to work with Realtime APIs by adding .connect(), .subscribe(), .sendMessage(), and .onWebSocket() methods. It also handles reconnect logic.

Note: Read the Likha ERP Realtime quickstart.

Global APIs

To keep the SDK dependency-free, it does rely on the APIs mentioned below, which originally came from the browser ecosystem and may not be available in all environments.

The fetch API

This API is shipped with almost every modern runtime. Nevertheless, there might be reasons to overwrite or set the implementation, for example, if an alternative implementation is preferred or if you actually work with a special runtime where fetch is not available.

The URL API

This API is shipped with almost every modern runtime. However, there are exceptions, like react-native, that require a polyfill for the SDK to work.

The WebSocket API

This API is optional if you're not making use of the realtime() features in the SDK. Backend JavaScript environments often do not ship with an implementation of WebSockets.

The logger API

This API is optional and currently only used for debugging realtime() features. This will default to the Console however in environments where this isn't shipped you can overwrite this with any logger.

Polyfilling

There are two polyfilling approaches, with the first taking precedence.

Options Parameter of createLikha

import { createLikha } from '@likha-erp/likha-sdk';
import { ofetch } from 'ofetch';
import WebSocket from 'ws';

const likha = createLikha('http://api.likha.example.com', {
  globals: {
    WebSocket: WebSocket,
    fetch: ofetch,
  }
});

globalThis object

import { createLikha } from '@likha-erp/likha-sdk';
import { ofetch } from 'ofetch';
import WebSocket from 'ws';

globalThis.WebSocket = WebSocket;
globalThis.fetch = ofetch;

import 'react-native-url-polyfill/auto';

const likha = createLikha('http://api.likha.example.com');

Polyfill libraries will often register itself to the globalThis object. For example, the react-native-url-polyfill package.

Error Handling

isLikhaError type guard

The SDK exports an isLikhaError type guard utility function to determine if the error thrown was from the API

import { createLikha, rest, isLikhaError, readItems } from '@likha-erp/likha-sdk';

const likha = createLikha('http://api.likha.example.com').with(rest());

try {
  const request = await likha.request(readItems('posts'));
} catch(error){
  if(isLikhaError(error)){
    // some error has been returned from the API
  } else {
    // some unknown non API error has been thrown (e.g. unable to parse the JSON response)
  }
}