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

@hos-ai/sdk

v0.1.0-alpha.1

Published

Types, validation and processing rules for HOS Core 0.1 and HOS Events 0.1, with signed producer manifests.

Readme

@hos-ai/sdk

TypeScript types, validation and processing rules for HOS 0.1, an open draft standard for hotel operations events: HOS Core for the entities, HOS Events for the facts that systems publish about them. The SDK also creates facts with stable ids, and signs and verifies producer manifests. It runs in Node and in a browser.

Draft and alpha. HOS 0.1 is a draft, 0.1.0-draft.2, and this package is an alpha: its API may still change. The versions stay 0.1.0-alpha.N until HOS 0.1 is final, and the compatibility policy applies from then on.

The same checks are available from the command line in @hos-ai/cli.

Install

Node.js 22 or later, or a current browser through a bundler. The package is ESM only.

npm install @hos-ai/sdk

It has three entry points:

| Import | What it holds | | :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | @hos-ai/sdk | types, validation, processing rules, fact creation and manifest signing. The schemas are embedded: no file is read, so it runs in a browser too. | | @hos-ai/sdk/node | loaders for a conformance scenario published as files: loadScenario, loadExpectedOutcome | | @hos-ai/sdk/reference | replayArrivalReadiness, the arrival-readiness reference projection. It is non-normative. |

Validate

validate recognises an event, a producer manifest or a reference situation, and checks it against the HOS 0.1 schemas:

import { validate } from "@hos-ai/sdk";

const result = validate(JSON.parse(text));
if (!result.valid) {
  for (const error of result.errors) console.log(`${error.path}: ${error.message}`);
}
/data/current: data.current is "cleaning", not one of: dirty, clean, inspected, unknown. Readiness as reported by the housekeeping authority.

result.kind says what the document is ("event", "manifest" or "situation"), and each error has the rule of the specification it breaks. The types of HOS documents are exported, generated from the schemas: HosFact, ProducerManifest, and one type per event, such as UnitStatusChanged. Give a document its type once validate accepts it.

validateStream checks a stream in JSON Lines, and the events against their producers' manifests:

import { validateStream } from "@hos-ai/sdk";

const stream = validateStream(jsonl, { manifests: [pms, housekeeping, messaging] });
for (const issue of stream.issues) console.log(`line ${issue.line} ${issue.severity}: ${issue.message}`);
line 5 info: Repeats line 4: same source, id and content. A consumer discards it as a duplicate.
line 7 error: urn:hos:pms:demo does not declare housekeeping.task.created at prop_demo: a consumer ignores this event (undeclared_capability).

Create facts

A producer turns what its system knows into HOS facts. HOS ids are opaque, never the source system's own ids, so they survive a migration of that system: the identity registry keeps the crosswalk between the two. And the same source event always gets the same fact id, so a consumer discards a redelivery as a duplicate.

import { createFactWriter, createIdentityRegistry } from "@hos-ai/sdk";

// Keep the crosswalk from one run to the next, so the same room keeps the same HOS id.
const ids = createIdentityRegistry(savedCrosswalk);
const facts = createFactWriter({
  source: "urn:hos:housekeeping:example",
  tenant: "tenant_example",
  propertyId: "prop_example",
  timezone: "Europe/Paris",
  recordedAt: new Date().toISOString(),
  idPrefix: "hk",
});

const unit = ids.resolve("unit", "ROOM-204");
facts.publish("unit.status_changed", "ROOM-204", "2026-07-30T06:40:00Z", [`unit:${unit}`], {
  unit_id: unit,
  dimension: "housekeeping",
  current: "dirty",
  authority_source: "urn:hos:housekeeping:example",
});

facts.events; // valid HOS facts, with every envelope attribute filled in

Process facts

HOS Events 0.1 asks every consumer to decide the same thing for each delivery: apply it, or discard it as a duplicate, superseded by a later fact, non_authoritative, or undeclared_capability. The SDK exports the rules behind those decisions:

  • factKey is the identity of a fact, its source and id, on which consumers deduplicate;
  • isLater and byOccurrence order facts by occurrence time, then source, then id;
  • findDeclaration finds the manifest entry that declares a fact for its property;
  • authority says whether a fact comes from the declared authority, from another declared producer, or from nobody who declared it.

replayArrivalReadiness, in @hos-ai/sdk/reference, applies them to a whole stream and reads arrivals from it:

import { replayArrivalReadiness } from "@hos-ai/sdk/reference";

const steps = replayArrivalReadiness(events, manifests, { ready_housekeeping_statuses: ["clean", "inspected"] });
for (const step of steps)
  console.log(
    step.delivery,
    step.disposition,
    step.emitted.map((situation) => situation.type),
  );
9 non_authoritative []
10 applied [ 'arrival.room_readiness_at_risk' ]
11 superseded []
12 applied [ 'arrival.room_readiness_resolved' ]

The dispositions follow the normative rules. The readiness of each stay and the situations are one reading of arrivals, the reference projection, which is non-normative: an implementation may assess arrivals differently and still conform.

Check a producer

checkProducer checks what a producer publishes: its manifest is valid and states its limitations, each fact is valid, published under the manifest's producer and declared for its property, no source and id name two different facts, and a redelivery of the same data brings back the same facts with the same ids.

import { checkProducer } from "@hos-ai/sdk";

const check = checkProducer({ manifest, recording, redelivery });
console.log(check.valid, check.recording.issues);

recording and redelivery are JSON Lines, as a producer would record them.

Sign and verify a manifest

A producer signs its manifest with a detached JWS on its canonical form (RFC 8785), with Ed25519 or ES256, and publishes the public key in a key set:

import { generateManifestKey, signManifest, verifyManifest } from "@hos-ai/sdk";

const { privateJwk, publicJwk } = await generateManifestKey(); // Ed25519, or { alg: "ES256" }
const jws = await signManifest(manifest, privateJwk, { expiresAt: new Date(Date.now() + 90 * 24 * 3600 * 1000) });

const result = await verifyManifest(manifest, jws, { keys: [publicJwk] });
console.log(result.valid ? `signed with ${result.header.kid}` : `${result.error}: ${result.message}`);

A failed verification gives its reason in result.error: malformed, unsupported_algorithm, unknown_key, unusable_key, bad_signature, expired or not_yet_valid. A manifest changed after signing gives:

bad_signature: The signature does not match this manifest and key …: the manifest changed after it was signed, or another key signed it.

Keep the private key secret, and out of version control.

Conformance scenarios

HOS publishes conformance scenarios: a stream of deliveries from several producers, their manifests, and the expected outcome of each delivery. In Node, loadScenario(directory) reads one, and toExpectedOutcome turns replay steps into the same shape as expected.json. To test an implementation in another language, use hos conformance run from @hos-ai/cli.

Problems and questions

Open an issue with the template for the CLI and the SDK: the package version, node --version, your system, a short piece of code that shows the problem, and what it prints. Use synthetic data only: never real guest data, credentials or private keys.

Links

License

Apache-2.0, like the HOS schemas the package embeds. The text of the specification is published under CC BY 4.0 on the HOS website and is not part of this package.