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

@syllecta/sdk-js

v0.1.0

Published

Universal TypeScript SDK for the Syllecta Cloud API

Readme

@syllecta/sdk-js

Official TypeScript SDK for the Syllecta platform. It wraps every public REST and webhook endpoint with safe defaults so product teams can send idempotent writes, forward third‑party webhooks, and monitor retries without re‑implementing middleware.


Why this SDK exists

Building reliable event ingestion on your own is tedious. Common issues:

  • Users double‑submit checkout forms and your API creates duplicate payments.
  • Webhooks arrive twice or out of order because Stripe/GitHub retries.
  • Each team builds its own retry + verification logic and it drifts out of sync.

@syllecta/sdk-js centralizes those patterns so that any Node.js service can:

  1. Call /v1/record with an idempotency key – duplicate requests are automatically deduped.
  2. Verify and forward webhooks to Syllecta – we keep the audit trail, you keep the business logic.
  3. Use typed HTTP calls with retry defaults without wiring your own client stack.

The SDK supports both ESM and CommonJS projects, works with plain fetch or Express, and exposes only the surface area customers need (no backoffice or admin APIs).


Prerequisites

Before coding, make sure you have:

  • A Syllecta API key (created in the dashboard).
  • Optional webhook signing secrets from Stripe/Shopify/etc. if you plan to verify signatures locally.
  • Node.js ≥ 20 and TypeScript ≥ 5.6 (JavaScript projects work too, but types give you extra safety).

Installation

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

If you want the built‑in Express middleware for webhooks:

npm install express body-parser

Configuring the client

import { CloudSDK, BearerAuth } from "@syllecta/sdk-js";

const sdk = new CloudSDK({
  baseUrl: "https://api.syllecta.com/v1",
  auth: BearerAuth(process.env.SYLLECTA_API_KEY!),
  timeoutMs: 30_000,
  maxRetries: 2
});

Available auth strategies:

| Helper | When to use | | ------------ | ------------------------------------------------------------------------- | | BearerAuth | Standard Syllecta API key | | ApiKeyAuth | Generic custom-header helper for non-standard/internal integrations | | NoAuth | Local/testing calls where auth is intentionally disabled |

For Syllecta tenant API keys, use Authorization: Bearer <ck_...> via BearerAuth(...). ApiKeyAuth(...) remains available only for custom gateways or internal compatibility layers.


Making idempotent REST calls

Idempotent writes guarantee that retries do not double‑book inventory, charge customers twice, or create duplicate database rows.

  1. Decide on a scope (usually METHOD:path).
  2. Generate a deterministic idempotency key (order_456_payment_attempt).
  3. Send the request with the helper or plain client.
const result = await sdk.idempotency.checkOrLock({
  key: "order_456_payment_attempt",
  scope: "POST:/payments",
  ttlSeconds: 60
});

if (result.cached) {
  // Another worker already processed this payment.
  return result.record;
}

if (!result.locked) {
  return { retryAfterMs: result.retryAfterMs ?? 250 };
}

// Your business operation (DB write, external API call, etc.)
const payment = { id: "pay_01", status: "accepted" };

await sdk.idempotency.save({
  key: "order_456_payment_attempt",
  scope: "POST:/payments",
  status: 201,
  headers: { "content-type": "application/json" },
  body: payment
});

Most teams use sdk.idempotency.* directly for lock/save flows; sdk.client.request remains available when you need low-level control.

Plain HTTP request example:

import { SyllectaApiError } from "@syllecta/sdk-js";

try {
  const res = await sdk.client.request<{ cached: boolean }>({
    method: "GET",
    path: "/v1/record",
    query: { key: "order_456_payment_attempt", scope: "POST:/payments" }
  });

  console.log(res.data.cached);
} catch (err) {
  if (err instanceof SyllectaApiError) {
    throw new Error(`Syllecta request failed with status ${err.status}`);
  }
  throw err;
}

How Syllecta responds

| Status | Meaning | Action | | ----------------------------- | ---------------------------------------------- | -------------------------- | | 201 | First successful write | Continue | | 200 with { cached: true } | Duplicate request | Safe to ignore | | 400 | Missing key / invalid body | Fix payload | | 401 | API key invalid | Rotate credentials | | 425 | Another request with same key is still running | Retry after retryAfterMs | | 429 | Usage quota exceeded | Check dashboard usage tab |


Chargeback simulation API

Use this endpoint when you need realistic dispute lifecycle events in sandbox/QA flows.

const simulation = await sdk.simulations.createChargeback({
  provider: "stripe",
  amount: 2500, // cents
  currency: "usd",
  transactionId: "txn_123",
  simulateLifecycle: true,
  finalStatus: "won" // optional: "won" | "lost"
});

console.log(simulation.simulation.id, simulation.simulation.status);

Request body fields:

  • provider: "stripe" | "braintree" | "adyen"
  • amount: integer in cents
  • currency: ISO currency code (usd, eur, ...)
  • transactionId: optional external transaction ID
  • simulateLifecycle: optional (true by default)
  • finalStatus: optional forced final state (won or lost)

Webhook processing playbook

Most customers integrate Syllecta in front of Stripe, Shopify, GitHub, or custom HMAC providers. The SDK exposes two levels of abstraction:

1. Raw forwarding

import { forwardWebhookEvent } from "@syllecta/sdk-js";

app.post("/shopify/webhook", async (req, res) => {
  const forwarded = await forwardWebhookEvent({
    provider: "shopify",
    apiKey: process.env.SYLLECTA_API_KEY!,
    body: req.body,
    headers: {
      "x-shopify-hmac-sha256": req.get("x-shopify-hmac-sha256") ?? undefined
    },
    rawBody: req.rawBody // strongly recommended
  });

  if (forwarded.cached) {
    return res.status(200).json({ ok: true, cached: true });
  }

  res.json({ ok: true });
});

Use this when you only need deduplication and centralized logging.

forwardWebhookEvent and handleWebhookEvent send tenant API keys as Authorization: Bearer <ck_...> by default. Pass apiKeyHeader only for explicit legacy/custom compatibility layers.

2. Full verification + processing

import { handleWebhookEvent } from "@syllecta/sdk-js";

app.post("/stripe/webhook", async (req, res) => {
  const { skipped } = await handleWebhookEvent({
    provider: "stripe",
    apiKey: process.env.SYLLECTA_API_KEY!,
    signingSecret: process.env.STRIPE_SIGNING_SECRET!,
    signatureHeader: "stripe-signature",
    headers: { "stripe-signature": req.get("stripe-signature") ?? undefined },
    rawBody: req.rawBody,
    body: req.body,
    onProcess: async () => {
      await processStripeEvent(req.body); // only runs once per event
    }
  });

  res.json({ ok: true, cached: skipped });
});

handleWebhookEvent returns early when the payload is already known to Syllecta, so your onProcess callback only executes for fresh events.


Pagination pattern

The SDK does not include an auto-paginator helper. For cursor endpoints, iterate manually:

import { SyllectaApiError } from "@syllecta/sdk-js";

type AuditLog = { id: string; occurredAt: string; actor: string };

let cursor: string | undefined;
do {
  try {
    const res = await sdk.client.request<{ items: AuditLog[]; nextCursor?: string }>({
      method: "GET",
      path: "/v1/audit/logs",
      query: { tenantId: "tenant_1", pageSize: 100, cursor }
    });

    for (const row of res.data.items) {
      console.log(row.id, row.actor);
    }

    cursor = res.data.nextCursor;
  } catch (err) {
    if (err instanceof SyllectaApiError) {
      throw new Error(`Failed to load audit logs (${err.status})`);
    }
    throw err;
  }
} while (cursor);

Working with errors

sdk.client.request returns { status, headers, data } only for successful responses. Any non‑2xx response throws SyllectaApiError, which includes status, headers, and body. Transport/runtime failures (network, timeout abort, etc.) still throw their native error.

import { SyllectaApiError } from "@syllecta/sdk-js";

try {
  const res = await sdk.client.request({
    method: "POST",
    path: "/v1/record",
    body: { key: "order_456_payment_attempt", scope: "POST:/payments" }
  });

  console.log("record status", res.status);
} catch (err) {
  if (err instanceof SyllectaApiError) {
    console.error("Syllecta API returned error", err.status, err.body);
  } else {
    console.error("Transport error", err);
  }
}

If you want a reusable type guard:

import { isSyllectaApiError } from "@syllecta/sdk-js";

try {
  await sdk.simulations.createChargeback({
    provider: "stripe",
    amount: 2500,
    currency: "usd"
  });
} catch (err) {
  if (isSyllectaApiError(err)) {
    console.error("status", err.status);
    console.error("body", err.body);
    throw err;
  }
  throw err;
}

Common statuses:

| Status | When it happens | What to do | | ------ | ------------------------------ | ------------------------------------- | | 400 | Header missing or JSON invalid | Regenerate request | | 403 | Tenant disabled | Visit dashboard to re‑enable | | 409 | Same key but different payload | Ensure you retry with identical body | | 425 | Another worker holds the lock | Retry after retryAfterMs | | 500 | Temporary platform issue | SDK auto‑retries (up to maxRetries) |


Local testing tips

  • Raw body: when using Express, configure bodyParser.json({ verify }) to stash the raw buffer on req.rawBody. Syllecta needs it to recompute signatures exactly as the provider sent them.
  • Replay protection: set the SYLLECTA_API_KEY from a sandbox tenant so you can replay events without affecting production dashboards.
  • Timeouts: adjust timeoutMs and maxRetries in the client constructor if you are testing over high‑latency tunnels.

Frequently used helpers

| Helper | Description | | --------------------- | ---------------------------------------------------------------- | | CloudSDK | High-level API (client, idempotency, webhooks) | | HttpClient | Low-level typed HTTP client with retry + typed API errors | | SyllectaApiError | Non‑2xx API error with status, headers, and parsed body | | forwardWebhookEvent | Sends a webhook payload straight to Syllecta | | handleWebhookEvent | Verifies + forwards + conditionally runs business logic | | verifySignature | Validates timestamped HMAC headers for inbound webhooks | | webhookVerifier | Express middleware for validating signatures before JSON parsing |

Each helper ships with TypeScript definitions so your IDE can autocomplete parameters and responses.


Example integration blueprint

Putting everything together for a typical SaaS backend:

import express from "express";
import bodyParser from "body-parser";
import { CloudSDK, BearerAuth, handleWebhookEvent } from "@syllecta/sdk-js";

const sdk = new CloudSDK({
  baseUrl: "https://api.syllecta.com/v1",
  auth: BearerAuth(process.env.SYLLECTA_API_KEY!)
});

const app = express();
app.use(bodyParser.json({ verify: (req: any, _, buf) => (req.rawBody = buf) }));

app.post("/orders", async (req, res) => {
  const key = `order_${req.body.orderId}`;
  const scope = "POST:/orders";

  const lock = await sdk.idempotency.checkOrLock({
    key,
    scope,
    ttlSeconds: 120
  });

  if (lock.cached && lock.record) {
    return res.status(lock.record.status).json(lock.record.body);
  }

  if (!lock.locked) {
    return res.status(425).json({ retryAfterMs: lock.retryAfterMs ?? 250 });
  }

  // Your domain operation
  const order = { orderId: req.body.orderId, accepted: true };

  await sdk.idempotency.save({
    key,
    scope,
    status: 201,
    headers: { "content-type": "application/json" },
    body: order
  });

  res.status(201).json(order);
});

app.post("/stripe/webhook", async (req, res) => {
  await handleWebhookEvent({
    provider: "stripe",
    apiKey: process.env.SYLLECTA_API_KEY!,
    signingSecret: process.env.STRIPE_SIGNING_SECRET!,
    signatureHeader: "stripe-signature",
    headers: { "stripe-signature": req.get("stripe-signature") ?? undefined },
    rawBody: req.rawBody,
    body: req.body,
    onProcess: () => processStripeEvent(req.body)
  });
  res.json({ ok: true });
});

app.listen(3000, () => console.log("Syllecta integration ready on :3000"));

This blueprint:

  • Accepts REST writes with idempotency keys.
  • Forwards Stripe webhooks and only processes them once.
  • Keeps all errors observable via structured logs.

Development workflow (for contributors)

pnpm install
pnpm run dev        # watch mode with tsup
pnpm test           # vitest
pnpm run build      # produces ESM + CJS bundles in dist/

Publishing:

pnpm version patch        # or minor/major
pnpm run build
pnpm publish --access public

Requirements

  • Node.js >= 20
  • TypeScript >= 5.6 (optional but recommended)
  • Express >= 4 if you use the provided middleware

License

MIT © 2026 Syllecta