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

zkbiotime-sdk

v0.1.1

Published

Typed, isomorphic TypeScript client for the ZKBioTime / BioTime 8 REST API (personnel, iclock devices, attendance).

Downloads

50

Readme

zkbiotime-sdk

Typed, isomorphic TypeScript client for the ZKBioTime / BioTime 8 REST API — personnel, iClock devices, and attendance reports.

  • Fully typed and runtime-validated — model types are inferred from Zod schemas, and every response is validated at runtime (opt out with validate: false).
  • 🔁 Pagination built inlist, listAll, and async-iterable paginate.
  • 🔐 HTTP Basic auth — the scheme that passes BioTime's IsNotOpenAPI license gate on trial and full licenses.
  • 🌐 Isomorphic — runs on Node 18+ and modern browsers (uses global fetch); one dependency (zod).
  • 🧯 Helpful errorsZKBioTimeError (status + body + license-gate flag) and ZKBioTimeValidationError (Zod issues + the raw data).
npm install zkbiotime-sdk

Quick start

import { ZKBioTimeClient } from "zkbiotime-sdk";

const zk = new ZKBioTimeClient({
  baseUrl: "http://10.10.10.218", // your BioTime server
  username: "admin",
  password: "••••••",
});

// List (one page) — fully typed
const { count, data } = await zk.employees.list({ page_size: 50, search: "somchai" });

// Create — only emp_code, department and area are required
const emp = await zk.employees.create({
  emp_code: "1001",
  department: 1,
  area: 1,
  first_name: "Somchai",
  mobile: "0812345678",
});

// Update (PATCH by default), then delete
await zk.employees.update(emp.id, { position: 2 });
await zk.employees.remove(emp.id);

// Iterate every punch across all pages, no manual paging
for await (const punch of zk.transactions.paginate({ start_time: "2026-06-01 00:00:00" })) {
  console.log(punch.emp_code, punch.punch_time);
}

Resources

| Namespace | Endpoint | Operations | |---|---|---| | zk.employees | /personnel/api/employees/ | CRUD + adjustArea, adjustDepartment, adjustPosition, adjustResign, resyncToDevice, delBioTemplate | | zk.departments | /personnel/api/departments/ | CRUD | | zk.areas | /personnel/api/areas/ | CRUD | | zk.positions | /personnel/api/positions/ | CRUD | | zk.resigns | /personnel/api/resigns/ | CRUD | | zk.terminals | /iclock/api/terminals/ | CRUD + reboot, uploadAll, clearCommand | | zk.transactions | /iclock/api/transactions/ | list / get / delete (raw punches aren't writable) | | zk.reports | /att/api/<report>/ | get(name, params), export(name, params) — name autocompletes |

Every namespace exposes list / listAll / paginate (where applicable):

const allDepts = await zk.departments.listAll();          // every page → array
const firstPage = await zk.departments.list({ page: 1 }); // raw { count, next, previous, data }

Reports

zk.reports.get() autocompletes the ~40 known report names (and still accepts any string):

const report = await zk.reports.get("monthlyPunchReport", {
  start_date: "2026-06-01",
  end_date: "2026-06-30",
  departments: "1,2",
});

Errors

Any non-2xx response throws ZKBioTimeError:

import { ZKBioTimeError } from "zkbiotime-sdk";

try {
  await zk.employees.list();
} catch (err) {
  if (err instanceof ZKBioTimeError) {
    console.error(err.status, err.body);
    if (err.isLicenseGate) {
      // 403 on /personnel/ or /iclock/: the IsNotOpenAPI gate.
      // Use Basic auth (this SDK does) or enable the `api` license mod.
    }
  }
}

Runtime validation

Types alone are a compile-time promise; this SDK also validates every response at runtime against Zod schemas, so the data you receive really matches its type. The schemas are the single source of truth — the exported model types are z.inferred from them.

import { ZKBioTimeValidationError } from "zkbiotime-sdk";

try {
  const emp = await zk.employees.get(123); // parsed + validated
} catch (err) {
  if (err instanceof ZKBioTimeValidationError) {
    console.error(err.path, err.issues); // Zod issues + err.data (the raw response)
  }
}

Schemas are deliberately lenient — unknown/forward-compatible fields pass through (typed unknown), and nullable fields are tolerated — so a server adding a column won't break you. Need maximum throughput or hitting a schema edge case? Turn it off:

const zk = new ZKBioTimeClient({ baseUrl, username, password, validate: false });

The schemas (EmployeeSchema, DepartmentSchema, …) and paginatedSchema() are exported if you want to validate elsewhere.

Authentication & the license gate

BioTime 8's iclock and personnel viewsets enforce an IsNotOpenAPI permission: requests carrying a JWT or DRF token get 403 unless the license includes the api mod (trial licenses don't). HTTP Basic auth leaves request.auth = None and passes the gate — so this SDK uses Basic auth by default. /att/ report endpoints have no gate.

Generic escape hatch

Anything not wrapped is one call away:

// Raw typed request
const data = await zk.request<{ count: number }>("GET", "/personnel/api/employees/", {
  query: { page_size: 1 },
});

// Schema probe: POST {} returns the required-field list and writes nothing
await zk.request("POST", "/personnel/api/employees/", { body: {} }).catch((e) => e.body);

Custom fetch

Pass your own fetch (e.g. with a self-signed-TLS agent on the LAN, or to route through a proxy):

const zk = new ZKBioTimeClient({ baseUrl, username, password, fetch: myFetch });

License

MIT