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

@softora/sdk

v1.0.1

Published

Official SDK for integrating products with Softora Central. Handles HMAC-SHA256 request signing, product registration, webhook verification, and SSO.

Readme

@softora/sdk

Official TypeScript SDK for integrating products with Softora Central.

Handles HMAC‑SHA256 request signing, product registration, webhook verification, and SSO authentication — so you can connect a new product to Softora with just your API keys and a few lines of code.

Zero Dependencies TypeScript Runtime


Installation

npm install @softora/sdk

Quick Start

import { SoftoraClient } from "@softora/sdk";

const softora = new SoftoraClient({
  apiKey: "sk_...",        // from Central admin panel
  sharedSecret: "sec_...", // from Central admin panel
  ssoSecret: "sso_...",    // from Central admin panel (optional)
});

// Register your product with Softora Central
const result = await softora.register({
  name: "My Awesome Product",
  dashboardUrl: "https://my-product.softora.cc",
  identifier: "my-product.softora.cc",
  webhookUrl: "https://my-product.softora.cc/api/webhooks/central-sync",
  endpoints: {
    webhooks: "https://my-product.softora.cc/api/webhooks/central-sync",
    health: "https://my-product.softora.cc/api/central-integration/health",
    users: "https://my-product.softora.cc/api/central-integration/users",
  },
});

console.log(`✅ Registered! Product ID: ${result.productId}`);
console.log(`🏢 Central company: ${result.centralBranding.centralCompanyName}`);

That's it. The SDK handles HMAC signing, nonce generation, timestamp injection, idempotency keys, and error handling automatically.


API Reference

new SoftoraClient(config)

Create a new SDK instance.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | apiKey | string | ✅ | — | Your API key from Central (sk_…) | | sharedSecret | string | ✅ | — | Your shared secret from Central (sec_…) | | ssoSecret | string | — | null | Your SSO secret from Central (sso_…) | | centralBaseUrl | string | — | "https://softora.cc" | Central server URL | | registerPath | string | — | "/api/product-manager/v1/register" | Registration endpoint path | | productUpdatedWebhookPath | string | — | "/api/webhooks/product-updated" | Webhook push endpoint path |


softora.register(options): Promise<RegisterResult>

Register or re‑register your product with Central.

First call: The API key is consumed and permanently locked to your product. Subsequent calls: Act as an upsert — updating endpoints, name, and logo.

const result = await softora.register({
  name: "Schedular",
  dashboardUrl: "https://schedular.softora.cc",
  identifier: "schedular.softora.cc",
  logoUrl: "https://schedular.softora.cc/logo.png",      // optional
  webhookUrl: "https://schedular.softora.cc/api/webhooks/central-sync", // optional
  endpoints: {                                             // optional
    webhooks: "https://schedular.softora.cc/api/webhooks/central-sync",
    health: "https://schedular.softora.cc/api/central-integration/health",
    users: "https://schedular.softora.cc/api/central-integration/users",
  },
});

Returns:

{
  productId: "uuid-...",
  centralBranding: {
    centralCompanyName: "Softora",
    centralCompanyLogoUrl: "/api/public/cms-assets/...",
  },
  message: "Product successfully registered and API key locked."
}

softora.verifyWebhook(rawBody, signature): Promise<VerifiedWebhook>

Verify an incoming webhook from Central. Uses constant‑time comparison to prevent timing attacks.

// Hono example
app.post("/api/webhooks/central-sync", async (c) => {
  const rawBody = await c.req.text();
  const signature = c.req.header("x-webhook-signature") ?? "";

  try {
    const event = await softora.verifyWebhook(rawBody, signature);
    
    if (event.event === "central.settings.updated") {
      const { companyName, logoUrl } = event.data;
      // Update your local branding...
    }
    
    return c.json({ success: true });
  } catch (err) {
    return c.json({ error: "Invalid signature" }, 403);
  }
});

Returns:

{
  event: "central.settings.updated",
  timestamp: "2026-06-06T12:00:00.000Z",
  data: { companyName: "Softora", logoUrl: "..." },
  verified: true  // type-level guarantee
}

softora.pushSettingsUpdate(productId, data): Promise<void>

Push a settings change to Central so the dashboard stays in sync.

await softora.pushSettingsUpdate("product-uuid", {
  productName: "New Product Name",
  productLogoUrl: "https://my-product.softora.cc/new-logo.png",
});

softora.verifySSOHeader(headerValue): true

Verify the x-central-sso-secret header on Central‑to‑Product integration requests.

// Hono middleware
app.use("/api/central-integration/*", (c, next) => {
  try {
    softora.verifySSOHeader(c.req.header("x-central-sso-secret"));
    return next();
  } catch {
    return c.json({ error: "Unauthorized" }, 401);
  }
});

Central Integration Endpoints

When Central connects to your product to manage users, it expects your product to implement specific REST endpoints at /api/central-integration/users.

The SDK provides the precise TypeScript interfaces you need to implement these endpoints properly without guessing the payload shapes:

import type { 
  CentralGetUsersResponse, 
  CentralCreateUserPayload,
  CentralUpdateUserPayload 
} from "@softora/sdk";

// Example: GET /api/central-integration/users
app.get("/api/central-integration/users", async (c) => {
  // 1. Fetch users from your database (Postgres, Mongo, D1, etc.)
  const dbUsers = await db.getUsers();

  // 2. Return them matching CentralGetUsersResponse
  const response: CentralGetUsersResponse = {
    success: true,
    data: {
      users: dbUsers.map(u => ({
        id: u.id,
        name: u.name,
        email: u.email,
        role: u.role,
        createdAt: u.createdAt,
      })),
      supportedUserTypes: ["ADMIN", "MEMBER"],
      userManagement: {
        title: "Add Product User",
        description: "Create a user directly in this product.",
        fields: [
          { name: "name", label: "Full Name", type: "text", required: true },
          { name: "email", label: "Email", type: "email", required: true },
          { name: "role", label: "Role", type: "role", required: true },
        ]
      }
    }
  };
  return c.json(response);
});

// Example: POST /api/central-integration/users
app.post("/api/central-integration/users", async (c) => {
  // Since fields are dynamic and controlled by your `userManagement.fields`, 
  // you pass your expected shape as a Generic to CentralCreateUserPayload:
  const payload = await c.req.json<CentralCreateUserPayload<{
    name: string;
    email: string;
    role: string;
  }>>();
  
  // Save to your DB!
  await db.insertUser(payload.name, payload.email, payload.role);
  
  return c.json({ success: true });
});

Error Handling

The SDK uses a typed error hierarchy. Every error has a machine‑readable code:

import { SoftoraError, SoftoraAuthError, SoftoraValidationError, SoftoraNetworkError } from "@softora/sdk";

try {
  await softora.register({ ... });
} catch (err) {
  if (err instanceof SoftoraAuthError) {
    // Invalid API key or signature (err.code, err.statusCode)
  } else if (err instanceof SoftoraValidationError) {
    // Missing required field (err.field)
  } else if (err instanceof SoftoraNetworkError) {
    // Central is unreachable (err.cause)
  } else if (err instanceof SoftoraError) {
    // Other Central API error (err.code, err.statusCode, err.responseBody)
  }
}

| Error Class | code examples | When | |-------------|----------------|------| | SoftoraAuthError | invalid_api_key, invalid_signature, expired_signature | Key/signature rejected | | SoftoraValidationError | validation_error | Missing or invalid input | | SoftoraNetworkError | network_error | DNS/connection failure | | SoftoraError | registration_failed, idempotency_key_conflict | Other Central errors |


Low‑Level Utilities

For advanced use cases, the SDK exports the cryptographic primitives:

import { sha256Hex, hmacSha256Hex, stableStringify, signRequest } from "@softora/sdk";

// Hash a string
const hash = await sha256Hex("hello world");

// Sign a custom request
const headers = await signRequest("POST", "/my/path", body, apiKey, sharedSecret);

Security Model

| Feature | Implementation | |---------|---------------| | Request signing | HMAC‑SHA256 with canonical string | | Replay protection | Single‑use nonces + 5‑minute timestamp window | | Idempotency | UUID‑based idempotency keys | | Webhook verification | HMAC‑SHA256 + constant‑time comparison | | SSO authentication | Shared secret header with constant‑time comparison | | Key storage | Only SHA‑256 hashes stored server‑side; raw keys never persisted | | Runtime compatibility | Web Crypto API only — no Node‑specific crypto module |


Runtime Compatibility

| Runtime | Version | Status | |---------|---------|--------| | Cloudflare Workers | Any | ✅ Full support | | Node.js | 18+ | ✅ Full support | | Deno | 1.x+ | ✅ Full support | | Bun | 1.x+ | ✅ Full support | | Browsers | Modern | ✅ Full support |


License

MIT © Softora