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

@syscli/yalidine

v0.1.0

Published

Typed Yalidine API client for Node. Server-side only.

Downloads

74

Readme

yalidine

A typed client for the Yalidine shipping API. It covers the parts every integration otherwise rewrites by hand: the auth headers, the rate-limit quota, pagination, batch parcel creation, and the fee math.

It is meant for servers, serverless functions, and edge runtimes. Your API id and token must never reach the browser. The Yalidine docs say so plainly, and this library does nothing to change that. Keep it on the server.

Published as @syscli/yalidine. It needs Node 18 or newer, uses the global fetch, and ships no runtime dependencies.

Install

npm install @syscli/yalidine

Quick start

import { Yalidine } from "@syscli/yalidine";

const yal = new Yalidine({
  id: process.env.YALIDINE_API_ID!,
  token: process.env.YALIDINE_API_TOKEN!,
});

// Reference data. Alger is 16, Sétif is 19, Batna is 5.
const wilayas = await yal.wilayas.list();
const desks = await yal.centers.list({ wilaya_id: 16 });

// Create a parcel and read back its tracking.
const created = await yal.parcels.create({
  order_id: "ORD-1043",
  from_wilaya_name: "Batna",
  firstname: "Amine",
  familyname: "Khelifi",
  contact_phone: "0661234567",
  address: "12 Rue Didouche Mourad",
  to_commune_name: "Alger Centre",
  to_wilaya_name: "Alger",
  product_list: "Casque audio",
  price: 8500,
  do_insurance: false,
  declared_value: 8500,
  length: 25,
  width: 20,
  height: 15,
  weight: 2,
  freeshipping: false,
  is_stopdesk: false,
  has_exchange: false,
});

console.log(created.tracking); // yal-XXXXXX

Quota

Yalidine returns how many requests you have left per second, minute, hour, and day on every response. The client reads those headers after each call, success or error, and keeps them on yal.quota:

await yal.wilayas.list();
console.log(yal.quota.perDay.left); // e.g. 9412

Each window starts undefined and fills in once the first response arrives. Watch these numbers. Accounts that ignore the limit get banned, which is also why the next section behaves the way it does.

Retry on 429, by default

When the API answers 429, the client waits the Retry-After it sends and tries again, up to three attempts total, before raising a RateLimitError. This is on out of the box because hammering past the limit is what gets an account banned, so a short pause is the safer default. Tune or switch it off through the constructor:

const yal = new Yalidine({
  id,
  token,
  retry: { attempts: 1, respectRetryAfter: true }, // 1 disables retrying
});

Pagination

list() returns a Page. Step through it with nextPage(), or let paginate() walk every page for you:

const first = await yal.parcels.list({ to_wilaya_id: 16 });
console.log(first.data, first.total, first.hasMore);

for await (const parcel of yal.parcels.paginate({ to_wilaya_id: 16 })) {
  console.log(parcel.tracking, parcel.last_status);
}

Fee quote

Fetch the fees for a wilaya pair, then total a real shipment for a commune. It applies the math for you: volumetric weight, the overweight surcharge past 5 kg, COD, and optional insurance.

const fees = await yal.fees.get({ from: 5, to: 16 }); // Batna to Alger

const quote = fees.quote(1601, {
  weight: 7,
  dimensions: { w: 40, h: 30, l: 50 },
  declaredValue: 12000,
  price: 12000,
  stopdesk: true,
  insurance: true,
});
// { delivery, overweight, cod, insurance, total }

The same functions are exported on their own (billableWeight, overweightFee, percentageFee) when you already hold the rates and want to skip the call.

Batch creation

createMany posts a batch and splits the result so you do not have to read the raw order-id map yourself:

const result = await yal.parcels.createMany([first, second]);

for (const ok of result.created) console.log(ok.tracking);
for (const bad of result.failed) console.warn(bad.orderId, bad.message);

result.byOrderId still holds the raw response for anything the split leaves out.

Validation

Parcels are checked before the request leaves. A missing stopdesk_id on a stop-desk delivery, a missing product_to_collect on an exchange, a price out of the 0 to 150000 range, or a malformed phone throws a ValidationError, keyed by order_id, and spends no request or quota:

import { ValidationError } from "@syscli/yalidine";

try {
  await yal.parcels.createMany(batch);
} catch (error) {
  if (error instanceof ValidationError) {
    for (const issue of error.issues) {
      console.warn(issue.orderId, issue.field, issue.message);
    }
  }
}

Errors

Every failure is a YalidineError, so you can catch one type and branch on it:

import { AuthError, NotFoundError, RateLimitError } from "@syscli/yalidine";

AuthError (401/403), NotFoundError (404), RateLimitError (429, with retryAfter), RequestError (other 4xx/5xx), and ValidationError (client-side) all carry status, a stable code, and the parsed response body.

Limitations

Worth knowing before you build on it:

  • Server only. The credentials grant full account access. Putting them in client-side code exposes them to anyone who opens the network tab. There is no browser build, and that is on purpose.
  • Masked PII in reads. GET and PATCH responses mask firstname, familyname, contact_phone, address, and the phone inside qr_text. Those are placeholders, not the real values. Never write them back to your own database. The masked fields carry a doc-comment as a reminder.
  • Rate limits and the ban. The per-second through per-day limits are real, and repeatedly blowing past them gets the account banned. The default retry helps; reading yal.quota helps more.
  • Edits are narrow. A parcel can only be updated or removed while its status is still en préparation. Once it moves on, the API rejects the change, and so will your update/remove call.

Development

npm install
npm test        # vitest, with msw mocking the API
npm run build   # tsup, dual ESM and CJS plus types

A small set of live tests run against the real API when YALIDINE_API_ID and YALIDINE_API_TOKEN are set, and are skipped otherwise.

License

MIT. See LICENSE.