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

@diffsome/sdk

v3.3.2

Published

Diffsome SDK for JavaScript/TypeScript - Different + Awesome

Readme

@diffsome/sdk

Diffsome Official SDK - Headless CMS + E-commerce + AI platform.

Version: 3.2.0

Installation

npm install @diffsome/sdk

Quick Start

import { Diffsome } from '@diffsome/sdk';

const client = new Diffsome({
  tenantId: 'your-tenant-id',
  apiKey: 'pky_xxxxxxxxxxxxxxxx',  // Required - Dashboard > Settings > API Tokens
  baseUrl: 'https://diffsome.com',  // Optional

  // Token auto-save (persistent login)
  persistToken: true,
  storageType: 'localStorage',  // or 'sessionStorage'
  onAuthStateChange: (token, user) => {
    console.log('Auth changed:', user);
  },
});

// Blog posts
const { data: posts } = await client.blog.list();

// Products
const { data: products } = await client.shop.listProducts();

Authentication (Auth)

Basic Setup

const client = new Diffsome({
  tenantId: 'demo',
  apiKey: 'pky_xxxxxxxxxxxxxxxx',  // Required
  persistToken: true,  // Auto-save token
});

Token Persistence

With persistToken: true:

  • Login: Auto-saved to localStorage
  • Refresh: Token auto-restored
  • Logout: Token auto-removed
  • Storage key: diffsome_auth_token_{tenantId}
const client = new Diffsome({
  tenantId: 'demo',
  apiKey: 'pky_xxx',
  persistToken: true,
  storageType: 'localStorage',  // Default, or 'sessionStorage'
  onAuthStateChange: (token, user) => {
    if (token) {
      console.log('Logged in:', user);
    } else {
      console.log('Logged out');
    }
  },
});

Login / Register

// Register
await client.auth.register({
  name: 'John Doe',
  email: '[email protected]',
  password: 'password',
  password_confirmation: 'password',
});

// Login (token auto-saved)
const { member, token } = await client.auth.login({
  email: '[email protected]',
  password: 'password',
});

// Logout (token auto-removed)
await client.auth.logout();

// Check auth status
client.auth.isAuthenticated();  // true/false

// Current user
const me = await client.auth.me();

// Update profile
await client.auth.updateProfile({ name: 'New Name' });

Social Login

// Available providers
const providers = await client.auth.getSocialProviders();
// ['google', 'kakao', 'naver']

// Get login URL
const { url } = await client.auth.getSocialAuthUrl('google');
window.location.href = url;

// Handle callback (after redirect)
const { member, token } = await client.auth.socialCallback('google', code);

Password Reset

// Send reset email
await client.auth.forgotPassword({ email: '[email protected]' });

// Reset password
await client.auth.resetPassword({
  token: 'reset-token',
  email: '[email protected]',
  password: 'newpassword',
  password_confirmation: 'newpassword',
});

Blog API

// List posts
const { data, meta } = await client.blog.list({
  page: 1,
  per_page: 10,
  category: 'tech',
  tag: 'laravel',
  search: 'keyword',
});

// Get post by slug
const post = await client.blog.get('post-slug');

// Featured posts
const featured = await client.blog.featured(5);

// Filter by category/tag
const { data } = await client.blog.byCategory('news');
const { data } = await client.blog.byTag('featured');

// Get categories/tags
const categories = await client.blog.categories();  // string[]
const tags = await client.blog.tags();  // string[]

Boards API

// List boards
const { data: boards } = await client.boards.list();

// Get board
const board = await client.boards.get('board-slug');

// List posts
const { data: posts } = await client.boards.listPosts('board-slug', {
  page: 1,
  per_page: 10,
  search: 'keyword',
});

// Get post
const post = await client.boards.getPost(postId);

// Create post (auth required)
await client.boards.createPost({
  board_id: 1,
  title: 'Title',
  content: 'Content',
  is_secret: false,
});

// Update/Delete post
await client.boards.updatePost(postId, { title: 'New Title' });
await client.boards.deletePost(postId);

Comments API

Supports board posts, blog posts, and standalone pages (guestbook).

// Board post comments
const { data } = await client.comments.boardPost(postId);
await client.comments.createBoardPost(postId, {
  author_name: 'John',
  content: 'Great!',
  password: '1234',  // Guest comment
});

// Blog post comments
const { data } = await client.comments.blogPost('post-slug');
await client.comments.createBlogPost('post-slug', {
  content: 'Nice article!',
});

// Standalone comments (guestbook)
const { data } = await client.comments.standalone('guestbook');
await client.comments.createStandalone('guestbook', {
  author_name: 'Visitor',
  content: 'Hello!',
});

// Common operations
await client.comments.update(commentId, { content: 'Updated' });
await client.comments.delete(commentId, { password: '1234' });
await client.comments.like(commentId);

Shop API

Products

// List products
const { data: products } = await client.shop.listProducts({
  category: 'shoes',
  is_featured: true,
  min_price: 10000,
  max_price: 50000,
  search: 'keyword',
});

// Get product by slug
const product = await client.shop.getProduct('product-slug');

// Featured products
const featured = await client.shop.featuredProducts(8);

// Categories
const { data: categories } = await client.shop.listCategories();

Product Types

// Filter by product type
const { data } = await client.shop.listProductsByType('digital');
const { data } = await client.shop.getDigitalProducts();
const { data } = await client.shop.getSubscriptionProducts();
const { data } = await client.shop.getBundleProducts();

Product Reviews

// List reviews
const { data: reviews, stats } = await client.shop.getProductReviews('product-slug', {
  page: 1,
  per_page: 10,
  rating: 5,  // Filter by rating
});

// Review stats
// stats = { average_rating: 4.5, total_count: 123, rating_counts: { 5: 80, 4: 30, ... } }

// Create review (auth + purchase required)
await client.shop.createReview('product-slug', {
  rating: 5,
  title: 'Great product!',
  content: 'Highly recommended.',
  images: ['https://...'],
});

// Check if can review
const { can_review, reason } = await client.shop.canReviewProduct('product-slug');

// Update/Delete review
await client.shop.updateReview(reviewId, { rating: 4 });
await client.shop.deleteReview(reviewId);

// Mark as helpful
await client.shop.markReviewHelpful(reviewId);

// My reviews
const { data: myReviews } = await client.shop.myReviews();

Cart

Works for both authenticated users and guests. Guest carts use session ID persistence.

// Get cart
const cart = await client.shop.getCart();

// Add item
await client.shop.addToCart({
  product_id: 1,
  quantity: 2,
  variant_id: 3,  // Variant option
});

// Update quantity
await client.shop.updateCartItem(itemId, { quantity: 3 });

// Remove item
await client.shop.removeFromCart(itemId);

// Clear cart
await client.shop.clearCart();

Wishlist (Auth Required)

// Get wishlist
const { data: items } = await client.shop.getWishlist();

// Add to wishlist
await client.shop.addToWishlist({
  product_id: 1,
  variant_id: 2,
  note: 'Birthday gift',
});

// Toggle wishlist (add if not in, remove if in)
const { action, in_wishlist } = await client.shop.toggleWishlist(productId);

// Check if in wishlist
const isInWishlist = await client.shop.isInWishlist(productId);

// Bulk check (for product list pages)
const wishlistStatus = await client.shop.checkWishlistBulk([1, 2, 3]);
// { '1': true, '2': false, '3': true }

// Wishlist count
const count = await client.shop.getWishlistCount();

// Move to cart
const { moved, failed, moved_count } = await client.shop.moveWishlistToCart();

// Update note
await client.shop.updateWishlistNote(wishlistId, 'New note');

// Remove from wishlist
await client.shop.removeFromWishlist(wishlistId);

Orders (Auth Required)

// Create order
const order = await client.shop.createOrder({
  orderer_name: 'John Doe',
  orderer_email: '[email protected]',
  orderer_phone: '010-1234-5678',
  shipping_name: 'John Doe',
  shipping_phone: '010-1234-5678',
  shipping_zipcode: '12345',
  shipping_address: 'Seoul, Gangnam',
  shipping_address_detail: 'Apt 101',
  shipping_memo: 'Leave at door',
  coupon_code: 'SAVE10',
});

// List orders
const { data: orders } = await client.shop.listOrders();

// Get order
const orderDetail = await client.shop.getOrder(orderId);

// Cancel order
await client.shop.cancelOrder(orderId);

Payments - Toss Payments

// Prepare payment
const payment = await client.shop.tossPaymentReady({
  order_number: 'ORD-123',
  success_url: 'https://mysite.com/payment/success',
  fail_url: 'https://mysite.com/payment/fail',
});
// { client_key, order_id, order_name, amount, customer_name, ... }

// Confirm payment (after Toss redirect)
const result = await client.shop.tossPaymentConfirm({
  payment_key: 'toss_payment_key',
  order_id: 'ORD-123',
  amount: 50000,
});

// Cancel payment
await client.shop.tossPaymentCancel('ORD-123', 'Customer request', 50000);

Payments - Stripe

// Create Checkout Session
const { session_id, checkout_url } = await client.shop.stripeCheckout({
  order_number: 'ORD-123',
  success_url: 'https://mysite.com/payment/success',
  cancel_url: 'https://mysite.com/payment/cancel',
});

// Redirect to Stripe
window.location.href = checkout_url;

// Verify payment (after Stripe redirect)
const result = await client.shop.stripeVerify({
  session_id: 'cs_xxx',
});

// Refund
await client.shop.stripeRefund('ORD-123', 'Customer request', 50000);

Payment Status

// Check available payment methods
const status = await client.shop.getPaymentStatus();
// {
//   toss: { available: true },
//   stripe: { available: true, publishable_key: 'pk_xxx' }
// }

Coupons

// Validate coupon
const result = await client.shop.validateCoupon('SAVE10', 50000);
// { valid: true, discount_amount: 5000, coupon: { ... } }

// My coupons
const coupons = await client.shop.myCoupons();

Digital Downloads (Auth Required)

// Get all my downloads
const downloads = await client.shop.getMyDownloads();

// Get downloads for specific order
const orderDownloads = await client.shop.getOrderDownloads('ORD-123');

// Get download URL
const downloadUrl = await client.shop.downloadFile(token);

// Get download info without downloading
const info = await client.shop.getDownloadInfo(token);
// {
//   id, token, file: { name, file_size_human, extension },
//   download_count, download_limit, remaining_downloads,
//   can_download, blocked_reason, expires_at
// }

Subscriptions (Auth Required)

// Get my subscriptions
const subscriptions = await client.shop.getSubscriptions();

// Get subscription detail
const subscription = await client.shop.getSubscription(id);
// {
//   id, product, plan: { interval, price, trial_days, features },
//   status, is_active, on_trial, current_period_end, ...
// }

// Create subscription (requires Stripe payment method)
const { subscription, client_secret } = await client.shop.createSubscription({
  plan_id: 1,
  payment_method_id: 'pm_xxx',
});

// Create setup intent for adding payment method
const { client_secret } = await client.shop.createSetupIntent();

// Cancel subscription
await client.shop.cancelSubscription(id);           // At end of period
await client.shop.cancelSubscription(id, true);     // Immediately

// Pause/Resume subscription
await client.shop.pauseSubscription(id);
await client.shop.resumeSubscription(id);

Bundle Products

// Get bundle items and pricing
const bundle = await client.shop.getBundleItems('product-slug');
// {
//   items: [{ product_id, name, quantity, unit_price, subtotal }],
//   original_total: 100000,
//   discount_type: 'percent',
//   discount_value: 20,
//   discount_amount: 20000,
//   final_price: 80000,
//   savings_percent: 20
// }

Reservation API

Public API

// Reservation settings
const settings = await client.reservation.getSettings();

// Services list
const services = await client.reservation.listServices();

// Staff list
const staff = await client.reservation.listStaff(serviceId);

// Available dates
const dates = await client.reservation.getAvailableDates({
  service_id: 1,
  staff_id: 2,
  start_date: '2026-01-01',
  end_date: '2026-01-31',
});

// Available time slots
const slots = await client.reservation.getAvailableSlots({
  service_id: 1,
  date: '2026-01-15',
  staff_id: 2,
});

Reservation Management (Auth Required)

// Create reservation
await client.reservation.create({
  service_id: 1,
  staff_id: 2,
  reservation_date: '2026-01-15',
  start_time: '14:00',
  customer_name: 'John Doe',
  customer_phone: '010-1234-5678',
  customer_email: '[email protected]',
  notes: 'Special request',
});

// My reservations
const { data } = await client.reservation.list({ status: 'confirmed' });

// Upcoming/Past reservations
const upcoming = await client.reservation.upcoming(5);
const past = await client.reservation.past(10);

// Cancel reservation
await client.reservation.cancel('RES-20260115-001', 'Schedule change');

Forms API

// List forms
const { data: forms } = await client.forms.list();

// Get form (includes field definitions)
const form = await client.forms.get('contact');

// Submit form
await client.forms.submit('contact', {
  name: 'John Doe',
  email: '[email protected]',
  message: 'Hello!',
});

// My submissions (auth required)
const { data: submissions } = await client.forms.mySubmissions();

Media API (Auth Required)

// Upload file
const media = await client.media.upload(file);

// Upload multiple files
const mediaList = await client.media.uploadMultiple([file1, file2]);

// My media list
const { data: myMedia } = await client.media.list({ type: 'image/jpeg' });

// Delete media
await client.media.delete(mediaId);

Custom Entities API

Dynamic data structure creation and management.

Entity Definition

// List entities
const entities = await client.entities.list();

// Get entity
const entity = await client.entities.get('customers');

// Create entity
await client.entities.create({
  name: 'Customer',
  slug: 'customers',
  schema: {
    fields: [
      { name: 'company', label: 'Company', type: 'text', required: true },
      { name: 'email', label: 'Email', type: 'email', required: true },
      { name: 'status', label: 'Status', type: 'select', options: [
        { value: 'active', label: 'Active' },
        { value: 'inactive', label: 'Inactive' },
      ]},
    ],
  },
});

// Update/Delete entity
await client.entities.update('customers', { name: 'Clients' });
await client.entities.delete('customers', true);  // force

Records

// List records
const { data: records } = await client.entities.listRecords('customers', {
  search: 'ACME',
  filters: JSON.stringify({ status: 'active' }),
});

// Get record
const record = await client.entities.getRecord('customers', 1);

// Create record
await client.entities.createRecord('customers', {
  company: 'ACME Corp',
  email: '[email protected]',
  status: 'active',
});

// Update/Delete record
await client.entities.updateRecord('customers', 1, { status: 'inactive' });
await client.entities.deleteRecord('customers', 1);

TypeScript Support

interface Customer {
  company: string;
  email: string;
  status: 'active' | 'inactive';
}

const customers = client.entities.typed<Customer>('customers');
const { data } = await customers.list();  // data: Customer[]

Response Types

All list APIs return consistent format:

interface ListResponse<T> {
  data: T[];  // Always array, never null
  meta: {
    current_page: number;
    last_page: number;
    per_page: number;
    total: number;
    from: number | null;
    to: number | null;
  };
}

Error Handling

import { Diffsome, DiffsomeError } from '@diffsome/sdk';

try {
  await client.auth.login({ email: '[email protected]', password: 'wrong' });
} catch (error) {
  if (error instanceof DiffsomeError) {
    console.log(error.message);  // "Invalid credentials"
    console.log(error.status);   // 401
    console.log(error.errors);   // { email: ["Invalid email or password"] }
  }
}

React Example

'use client';

import { useState, useEffect } from 'react';
import { Diffsome } from '@diffsome/sdk';

// Singleton client
const client = new Diffsome({
  tenantId: process.env.NEXT_PUBLIC_DIFFSOME_TENANT_ID!,
  apiKey: process.env.NEXT_PUBLIC_DIFFSOME_API_KEY!,
  persistToken: true,
});

function BlogList() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    client.blog.list({ per_page: 10 })
      .then(({ data }) => setPosts(data))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

Changelog

v3.2.0

  • Bundle product support
  • getBundleItems() API
  • Product type filter (listProductsByType())

v3.1.0

  • Subscription management API
  • getSubscriptions(), createSubscription(), cancelSubscription()
  • pauseSubscription(), resumeSubscription()
  • Stripe setup intent for payment method

v3.0.0

  • Breaking: Renamed from Promptly to Diffsome
  • Breaking: Storage key changed from promptly_auth_token_ to diffsome_auth_token_
  • Digital download API (getMyDownloads(), downloadFile())
  • Wishlist API (full CRUD + bulk operations)
  • Guest cart with session persistence
  • Stripe payment integration

v2.18.0

  • Product review API
  • Shipping settings API

v2.15.0

  • Toss Payments integration

v2.12.0

  • Blog category/tag filters
  • Added category, tags, views, published_at fields

v2.10.0

  • persistToken option for auto token storage
  • onAuthStateChange callback
  • storageType option

v2.5.0

  • Secret posts support (is_secret, is_mine)

v2.3.0

  • Polymorphic comments API (board, blog, standalone)

v2.0.0

  • Breaking: API key required

v1.3.0

  • ListResponse<T> unified format
  • Reservation system support

License

MIT