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

zklib-ng

v0.1.8

Published

Pure-TypeScript ZKTeco standalone-device client (TCP/UDP 4370) — users, attendance, templates, realtime, access control. No native DLL.

Readme

zklib-ng

Pure-TypeScript client for ZKTeco standalone biometric / time-attendance / access-control devices over TCP and UDP (port 4370). No native DLL, no zkemkeeper COM dependency — the wire protocol is reimplemented in TypeScript on a small, data-driven engine.

  • ✅ Works over TCP or UDP, any platform (Linux/macOS/Windows/ARM/Docker)
  • ✅ Users, attendance, fingerprint templates (read and upload), device info
  • ✅ Online fingerprint enrollment, access control, holidays, bells, work codes
  • ✅ Generic device-data table access — read/write/delete ~29 tables
  • Auto-reconnect + retry, event-style real-time (watch()), concurrency-safe
  • ✅ Cross-platform LAN discovery
  • ✅ Clean namespaced API, dual ESM + CJS, zero runtime deps, Node ≥ 18

A ground-up v2 of ZKLib-TS built on a primitive + registry architecture: adding a command is a data entry, not a new codec. Device-verified against firmware Ver 6.60 (platform ZLM60_TFT).

Install

npm install zklib-ng

Quick start

import { ZKDevice } from 'zklib-ng'

const zk = new ZKDevice({ host: '192.168.1.201' }) // port 4370, transport 'tcp'
await zk.connect()

console.log(await zk.device.firmware())   // "Ver 6.60 Oct 12 2021"
console.log(await zk.device.sizes())      // { users, fingers, records, ... }

const users = await zk.users.list()       // User[]
const logs = await zk.attendance.list()   // AttLog[] (Date timestamps)

await zk.disconnect()

Options

new ZKDevice({
  host: '192.168.1.201',
  port: 4370,            // default
  password: 0,           // comm key (default 0)
  timeoutMs: 10000,      // default
  transport: 'tcp',      // 'tcp' | 'udp'
  autoReconnect: false,  // rebuild + retry an op on network error
  reconnectRetries: 1,   // attempts when autoReconnect is on
})

Namespaced API

All methods are async. Identity uses string userId (the PIN); uid is the internal device slot.

zk.device.firmware() / serial() / fingerprintVersion() / time() / setTime(d) / sizes()
zk.options.get(key) / set(key, value)                          // device config (key=value)
zk.users.list() / get(userId) / set(user, packetSize?) / delete(uid)
zk.attendance.list() / clear()
zk.templates.list() / get(uid, fid) / set(user, fingers) / delete(uid, fid)
zk.tables.schemas() / read(name) / write(name, row) / delete(name, field, value)
zk.access.userGroup.get/set · timeZone.get/set · groupTimeZones.get/set · unlockGroup.get/set · holiday.get/set
zk.bell.set(schedule) / list()
zk.enroll.fingerprint({ userId, fingerId?, onProgress? }) / cancel() / verify() / regEvent(flags)
zk.oplog.list()
zk.photos.get(userId) / cards()    // id_card table (binary-safe photo blob)
zk.faces.list() / get(userId) / delete(userId)
zk.roles.list() · zk.wiegand.list()
zk.liveCapture(signal?)            // async generator of punch events
zk.liveEvents(opts?)               // async generator — multiplexed events (attendance/finger/raw)
zk.watch(opts?)                    // EventEmitter (see below)
ZKDevice.discover(opts?)           // static — find devices on the LAN

See docs/sdk-coverage.md for the full SDK capability map.

Real-time events

const w = zk.watch({ autoReconnect: true })
w.on('attendance', ev => console.log(ev.userId, ev.timestamp, ev.status, ev.punch))
w.on('reconnecting', ({ attempt, error }) => console.warn('drop, retry', attempt, error.message))
w.on('reconnect', () => console.log('reconnected'))
w.on('error', err => console.error('gave up', err)) // only after retries exhausted
w.on('end', () => console.log('stopped'))
// ... later
w.stop()                            // ends the watcher even with autoReconnect on

Each ZKDevice is independent (own socket, session, command queue), so one Node process can watch a whole fleet concurrently. Operations on a single instance are serialized internally — concurrent calls never corrupt the wire.

Discover devices on the LAN

const devices = await ZKDevice.discover()                  // [{ address, port }]
const detailed = await ZKDevice.discover({ enrich: true }) // + serialNumber, firmware

Fleet — manage many devices as one

ZKFleet absorbs the multi-device plumbing so the app stays thin: one merged realtime stream, broadcast user/template ops, error-isolated per device.

import { ZKFleet } from 'zklib-ng'

const fleet = new ZKFleet(['192.168.1.201', '192.168.1.202'], { autoReconnect: true })
await fleet.connectAll()

// one merged stream, each punch tagged with its device
fleet.watchAll().on('punch', ({ host, log }) => save(host, log))

// broadcast (error-isolated; returns a per-host { host, ok, error? }[])
await fleet.setUserEverywhere(user)
await fleet.distributeTemplate(user, fingers) // enroll once, push to all
await fleet.setTimeEverywhere(new Date())

// or build a fleet from discovery
const auto = await ZKFleet.discover()

Gapless attendance feed

With autoReconnect, watch() (and fleet.watchAll()) is gapless: on every reconnect it auto-pulls the log and emits the punches missed during the outage — newer than the last emitted, de-duplicated by attLogKey. The app writes zero catch-up logic; it just consumes attendance / punch events and stores them under a unique key:

import { attLogKey } from 'zklib-ng'
// e.g. DB unique key per punch → idempotent inserts (the device re-delivers logs)
db.upsert({ id: attLogKey(log), ...log })

Business logic (in/out evaluation, shift rules, scheduling) stays in the app.

Distribute a fingerprint across a fleet

Enroll once, then push the user + templates to every other device (templates are portable between devices reporting the same device.fingerprintVersion()):

await source.users.set(user)
await source.enroll.fingerprint({ userId: user.userId, fingerId: 0 }) // press 3×
const fingers = (await source.templates.list()).filter(f => f.uid === user.uid)
for (const zk of others) {
  await zk.users.set(user)
  await zk.templates.set(user, fingers)
}

Device support

Speaks the standalone ZK protocol on port 4370. Core (connect/auth, users, attendance, device info, templates, real-time) works broadly across BW/TFT/SSR firmware. Advanced features (device-data tables, access-control layouts, bell, enroll, template upload) were reverse-engineered and verified on firmware 6.60; other models may differ. Encrypted-channel firmware (newest models) is not supported.

ZK_HOST=192.168.1.201 npx ts-node examples/diagnose.ts
ZK_HOST=192.168.1.201 ZK_TRANSPORT=udp npx ts-node examples/diagnose.ts

Development

npm install
npm test          # unit tests (no device needed; mock TCP/UDP servers)
npm run build     # tsc -> dist/{cjs,esm,types}
npm run lint
npm run docs      # typedoc -> docs/api/ (markdown API reference)

Documentation

License

MIT.

Not affiliated with or endorsed by ZKTeco. "ZKTeco" is a trademark of its owner.