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

tailorade-sdk

v1.0.1

Published

Official TypeScript SDK for the Tailorade API — customers, products, patterns, embroidery, and salwar master data.

Readme

tailorade-sdk

Official TypeScript / Node.js SDK for the Tailorade API.

Features

  • Full TypeScript types for every resource
  • Guest (unauthenticated) browsing via a Public API Key
  • Unified sign-in / first-time registration via WhatsApp OTP
  • Server-side logout with token blacklisting
  • Client-side session management (SessionManager)
  • E.164 phone sanitisation utility (sanitizePhone)
  • OTP resend cooldown tracker (OtpCooldown)
  • Appointment Slots & Bookings resources
  • HSO Orders (Home Service Orders) — read-only
  • Auto-pagination helper
  • Filter builder helper
  • Automatic retry with exponential back-off (5xx / network errors)
  • Per-request timeout

Installation

npm install tailorade-sdk

Quick Start

import { TailoradeClient, sanitizePhone } from 'tailorade-sdk';

// Initialize with your Public API Key for guest access.
const client = new TailoradeClient({
  apiKey: process.env.PUBLIC_API_KEY,
});

// Browse the catalog without authentication.
const products = await client.products.list();
const slots = await client.appointmentSlots.list();

Authentication

Authentication Modes

| Mode | How to set | What it sends | Access | |---|---|---|---| | Guest | new TailoradeClient({ apiKey: '...' }) | x-api-key: <key> | Public read-only endpoints | | Authenticated | After auth.verifyOtp() | Authorization: Bearer <jwt> | All endpoints |

When a JWT is present, the API key is suppressed. The SDK always prefers the JWT.

Step 1 — Initiate sign-in (POST /auth/sign-in)

import { sanitizePhone } from 'tailorade-sdk';

const mobile = sanitizePhone('+91 98765 43210'); // → 9876543210
const { isNewCustomer } = await client.auth.signIn({ mobileNumber: mobile });
// isNewCustomer: true if a new account was created, false for existing customers.

Step 2 — Verify OTP (POST /auth/verify-otp)

const { token, customer } = await client.auth.verifyOtp({
  mobileNumber: 9876543210,
  otp: '123456',
});
// JWT is stored automatically — no extra setup needed.
console.log(client.isAuthenticated); // true
console.log(client.customerId);      // 'customer-uuid'

Logout (POST /auth/logout)

await client.logout();
// Token is blacklisted server-side and cleared client-side.
console.log(client.isAuthenticated); // false
// A new session ID is generated for the guest session.

Session Management

const client = new TailoradeClient({ apiKey: process.env.PUBLIC_API_KEY });

console.log(client.sessionId);      // Auto-generated UUID, useful for logging / analytics
console.log(client.isAuthenticated); // false (guest)

await client.auth.signIn({ mobileNumber: 9876543210 });
await client.auth.verifyOtp({ mobileNumber: 9876543210, otp: '123456' });

console.log(client.isAuthenticated); // true
console.log(client.customerId);       // 'cust-uuid'

Phone Sanitization

import { sanitizePhone } from 'tailorade-sdk';

sanitizePhone('+91 98765 43210') // → 9876543210
sanitizePhone('0091-9876543210') // → 9876543210
sanitizePhone('9876543210')      // → 9876543210
sanitizePhone('919876543210')    // → 9876543210

// Throws on invalid input:
sanitizePhone('1234') // Error: "Invalid phone number..."

OTP Cooldown Tracker

The backend enforces a 30-second resend cooldown. Mirror it on the client for a smooth UI:

import { OtpCooldown } from 'tailorade-sdk';

const cooldown = new OtpCooldown();

await client.auth.signIn({ mobileNumber: 9876543210 });
cooldown.start();

// In your UI tick / reactive effect:
setInterval(() => {
  if (cooldown.isActive) {
    setButtonLabel(`Resend in ${cooldown.remainingLabel}`); // e.g. "Resend in 29s"
  } else {
    setButtonLabel('Resend OTP');
  }
}, 1000);

Resources

Public Resources (API key — guest mode)

| Resource | Property | Endpoints | |---|---|---| | Products | client.products | list, get, create, update, delete, bulkDelete, export, import | | Pattern Catalogs | client.patternCatalogs | list, get, create, update, delete | | Pattern Designs | client.patternDesigns | list, get, create, update, delete | | Embroidery Catalogs | client.embroideryCatalogs | list, get, create, update, delete | | Salwars | client.salwars | list, get, create, update, delete | | Appointment Slots | client.appointmentSlots | list, get (public) · create, update, delete (admin) | | HSO Orders | client.orders | list, get (read-only) |

Authenticated Resources (JWT required)

| Resource | Property | Key Methods | |---|---|---| | Customers | client.customers | list, get, findByMobile, create, update, delete | | Appointment Bookings | client.appointmentBookings | list, get, listByCustomer, create, update, updateStatus, delete |


Appointment Slots

// Guest browsing
const slots = await client.appointmentSlots.list({
  filters: JSON.stringify({ isActive: true }),
});
slots.items.forEach((slot) => {
  console.log(slot.name, slot.time, `${slot.booked}/${slot.limit} booked`);
});

Appointment Bookings

// Create a booking (requires authentication)
const booking = await client.appointmentBookings.create({
  slotId: 'slot-uuid',
  customerId: client.customerId!,
  appointmentDateTime: '2026-07-07T10:00:00.000Z',
  paymentAmount: 450,
  discount: 50,
  freeBooking: false,
});

// List my bookings
const myBookings = await client.appointmentBookings.listByCustomer(client.customerId!);

// Cancel a booking
await client.appointmentBookings.updateStatus(booking.id, { status: 'Cancelled' });

HSO Orders (Home Service Orders)

// View order history (public — no login required)
const orders = await client.orders.list({
  filters: JSON.stringify({ 'customer.id': client.customerId }),
});

orders.items.forEach((order) => {
  console.log(order.hsoId, order.status, order.appointmentDate);
});

// Get a single order
const order = await client.orders.get('hso-uuid');

Configuration

const client = new TailoradeClient({
  baseUrl: 'https://backend.tailorade.in/api/v1', // default
  apiKey: process.env.PUBLIC_API_KEY,              // public read-only key
  token: storedJwt,                                // pre-load a JWT from previous session
  timeout: 10_000,                                 // ms, default 10s
  retries: 3,                                      // default 3 (exponential back-off)
});

Error Handling

All methods throw typed errors from tailorade-sdk/errors:

| Error class | HTTP status | |---|---| | ValidationError | 400 | | AuthenticationError | 401 | | AuthorizationError | 403 | | NotFoundError | 404 | | ConflictError | 409 | | RateLimitError | 429 | | ServerError | 500 | | ServiceUnavailableError | 503 | | NetworkError | network/timeout |

import { RateLimitError, AuthenticationError } from 'tailorade-sdk';

try {
  await client.auth.signIn({ mobileNumber: 9876543210 });
} catch (err) {
  if (err instanceof RateLimitError) {
    // Daily limit reached or 30-second cooldown active.
    console.error('Too many OTP requests:', err.message);
  } else if (err instanceof AuthenticationError) {
    // Invalid OTP.
  }
}

Rate Limits

| Limit | Scope | Error | |---|---|---| | 30-second cooldown | Per mobile number | RateLimitError (429) | | 10 OTPs per 24 hours | Per mobile number | RateLimitError (429) |

Use OtpCooldown on the client to prevent unnecessary API calls during the 30-second window.


Environment Variables

Add these to your .env:

# Provided by Tailorade — used by the customer-facing app for guest access.
PUBLIC_API_KEY=your_public_api_key_here

Development

npm install
npm run build
npm test

Tests use Vitest with MSW for API mocking.