Skip to content

feat(@hertzg/hikvision-api)!: Hikvision ISAPI client - #304

Merged
hertzg merged 9 commits into
mainfrom
hertzg/hikvision-api-sanitize-tests
Oct 25, 2026
Merged

hertzg merged 9 commits into
mainfrom
hertzg/hikvision-api-sanitize-tests

Conversation

@hertzg

@hertzg hertzg commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Adds @hertzg/hikvision-api: a small, typed, runtime-agnostic Hikvision ISAPI package for Deno and Bun.

  • request(device, path) returns the raw Response; request(device, resource) returns the decoded document.
  • stream(device, alertStream, { signal }) yields parsed alerts from /ISAPI/Event/notification/alertStream until the signal aborts. No reconnect, no timers, no motion-end inference.
  • Resources: device info, device time, input proxy channels and status, motion detection and its capabilities, smart rules (field detection, line detection, any region-type rule).
  • Every document is a codec description read by one engine; every failure is one IsapiError.
  • Fixtures use documentation addresses and generic names only.

Registered in the labeler, Release Please config and manifest, import map, README and AGENTS.md.

…evice info, channels and motion grid

Reads /ISAPI/Event/notification/alertStream as a ReadableStream of parsed
alerts and the device info, channel list, motion grid and smart rule
documents as typed objects. Basic auth, injectable fetch, no reconnects or
timers: the consumer owns backoff and the motion-end threshold, measured
from each alert's arrival time.
…ying on fetch

Against a real NVR the 30 s AbortSignal did not end the stream: fetch
kept the body open and the consumer's loop never returned. On abort the
stream now cancels its own reader, which resolves a pending read as done
and cancels the response body, so the loop ends and the connection drops
whatever fetch does with the signal.
…regions and generic smart rules

Verified against the ISAPI guides, the NVR web UI's command table and real
device output. Errors keep the device's reason on any HTTP status, motion
reads grid and polygons, smart paths take the channel, and the package
runs on Bun and Deno alike.
…nd export a single entry point

Each reader is now a document description read by one engine instead of
hand-written tree walking, and the package exports only its root: the
client, its types, and the parse functions. Sub-path imports such as
`@hertzg/hikvision-api/config/smart` no longer exist.

BREAKING CHANGE: all sub-path exports are gone; import from `@hertzg/hikvision-api`.
A failed request now throws one error whatever the shape of the failure:
HTTP status outside 2xx, a failing ResponseStatus body, or both. Its
fields are flat and optional, so a consumer branches on subStatusCode for
the reason and httpStatus for the transport without string matching.

BREAKING CHANGE: HttpError is gone; IsapiError carries url, httpStatus, statusCode, subStatusCode and statusString directly, and is constructed from one options object.
…s through one call

Modules now live under isapi/<area>/ named after the path they read, and
each exports a resource: a path plus a codec. The client reads any of them
with read(resource); the named methods are one-line wrappers.

BREAKING CHANGE: the parseX/readX/xPath functions are gone; use the exported resources (`deviceInfo`, `motionDetection({ channel })`, …) with `client.read` or `resource.codec.parse`.
…eam over a plain Device

Three layers of functions, no client object. A Device is where the device
is and how to log in; every call takes it first. request(device, path)
returns the raw Response; request(device, resource) returns the decoded
document; stream(device, resource, { signal }) returns a ReadableStream.
The alert stream is a resource like any other. http/ holds fetch, auth and
multipart; isapi/ holds the protocol and the documents.

A client with named methods promised every device serves every document.
It cannot: a camera answers notSupport. The vendor's own websdk.js is a
table of name to URL with get and set over it, the same shape as this.

BREAKING CHANGE: createClient, Client, openAlertStream, RequestOptions and
read are gone. Use request(device, resource), stream(device, alertStream,
{ signal }) and the Device type.
@github-actions

Copy link
Copy Markdown
Contributor

Docs preview: https://hertzg.github.io/jsr-monorepo/pr/304/

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

No significant changes detected. 31 benchmark(s) within their noise floor.

@codecov-commenter

codecov-commenter commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.14815% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.81%. Comparing base (86c6383) to head (23267e4).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/@hertzg/hikvision-api/isapi/stream.ts 83.82% 9 Missing and 2 partials ⚠️
.../@hertzg/hikvision-api/http/create-parts-stream.ts 95.58% 2 Missing and 1 partial ⚠️
.../@hertzg/hikvision-api/isapi/event/decode-alert.ts 97.22% 0 Missing and 2 partials ⚠️
...ikvision-api/isapi/status/parse-response-status.ts 94.44% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #304      +/-   ##
==========================================
+ Coverage   97.78%   97.81%   +0.02%     
==========================================
  Files         374      424      +50     
  Lines       13794    14712     +918     
  Branches     1458     1560     +102     
==========================================
+ Hits        13489    14390     +901     
- Misses        175      186      +11     
- Partials      130      136       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ents, tests on node:test

A field reads the element it is handed; decodeFields finds children by
name and owns required, optional and many. Lists no longer forge a parent
around each item, and the lookup helpers stop being exports. Every file
exports one function with its type beside it and its test next to it, so
the package reads like the tvali libraries it is written for. Tests run
on node:test, which Deno and Bun both execute.

BREAKING CHANGE: Field.decode takes an element, not a parent and a name;
children, child, textContent and decodeFields are no longer exported.
Public resources, request, stream and the types are unchanged.
@hertzg
hertzg merged commit 5728551 into main Oct 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants