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

@iveri/contracts

v0.20.0

Published

Wire contracts shared by every Iveri backend and frontend. Zero runtime dependencies.

Downloads

4,007

Readme

@iveri/contracts

The wire contract every Iveri service and frontend compiles against: shared types and plain enums, and nothing else.

Release 0.20.0 adds the notification contract under @iveri/contracts/notificationNotificationChannel, NotificationDeliveryStatus, NotificationTemplate, Notification with its per-channel NotificationDelivery list, InAppNotification and the submit body — plus six notification:* permissions. Two absences are decisions and each carries a spec: there is no push channel, because a channel a caller can name and nothing can deliver reads as a successful submit and reaches nobody; and no delivered status, because a relay accepting a message is not evidence a person received it and nothing subscribes to a bounce. Status lives on the delivery rather than the notification — one that emailed successfully and dead-lettered in the inbox has two true answers and no single one.

Release 0.19.0 adds MessageVariableInput — a declaration as a client sends it, where the optional halves may be absent. A separate type rather than MessageVariable with everything optional, because omitting a field on the way in and receiving null on the way out are different statements.

Release 0.18.0 adds the localization contract under @iveri/contracts/localizationLocale, Namespace, Message with its declared MessageVariable list, Translation, Release, the build-time TranslationBundle and the runtime render shapes — plus five localization:* permissions. Two consumers exist from day one and they read it differently: a frontend pulls a bundle at build time and compiles it in, a backend renders a message at runtime for a locale that belongs to the recipient rather than the caller.

Release 0.17.0 adds realtime.unsubscribe. Subscribing is additive and capped per socket, so without it a long-lived inbox that pages through conversations reaches the cap and then silently stops receiving anything.

Release 0.16.0 adds the Unibox realtime contract under @iveri/contracts/uniboxRealtimeConversationEvent, the publish body, the socket message shapes and the event-name constants — plus the unibox:realtime:publish permission. It is shared by three repos at once (unibox-api publishes, unibox-realtime fans out, unibox-web subscribes), which is exactly the case a hand-copied interface drifts on.

pnpm add @iveri/contracts
// Cross-service primitives.
import { CursorPage, ErrorCode, Maybe, Paginated, UUID, UserPermission } from '@iveri/contracts';

// One service's wire surface.
import type { CaptureSummary, Endpoint } from '@iveri/contracts/conduit';
import type { AuthSession, Principal } from '@iveri/contracts/identity';
import type { Conversation, Message } from '@iveri/contracts/unibox';
import type { Namespace, TranslationBundle } from '@iveri/contracts/localization';
import type { Notification, NotificationChannel } from '@iveri/contracts/notification';

Seven entry points, and no others — never @iveri/contracts/dist/....

The per-service surfaces are not re-exported from the root, deliberately. Conduit owns a Provider, a Connection and an Endpoint; Unibox owns a Channel and a Contact and would have wanted two of those names for entirely different things. Keeping each service behind its own entry point means that collision never has to be resolved by renaming a type in a shipped API — and it stopped being hypothetical the moment unibox was added.

Zero runtime dependencies — the rule that defines this package

This package ends up inside browser bundles, so it declares no dependencies, peerDependencies or optionalDependencies, and its compiled output imports nothing outside itself. Nothing from @nestjs/*, typeorm, class-validator, or node:* may ever appear here — not even as a type-only import.

That is why it is a separate package from @iveri/nest-sdk rather than a folder inside it. It is enforced by scripts/verify-zero-runtime-deps.mjs, which runs as part of build: CI fails on the PR that adds a dependency, not on a frontend build weeks later.

Anything needing a dependency belongs in @iveri/nest-sdk (backend) or @iveri/react-kit (frontend).

What is here

type/

| Type | Use | | ---------------------------------- | --------------------------------------------------------------------------------------------- | | Nil, Maybe<T>, Nullable<T> | absent values. Nullable for anything crossing the wire — JSON.stringify drops undefined | | UUID | template-literal shape hint; validation stays at the edge with @IsUUID() | | Paginated<T> | offset paging, where the client needs a total or arbitrary page jumps | | CursorPage<T> | cursor paging — the default for lists that grow while being read | | IsoDateTime | a timestamp as it appears on the wire. JSON has no date type — this is a string | | ApiErrorResponse, ApiErrorBody | the error envelope. Only errors are enveloped; a success is the bare payload |

enum/

ErrorCode — the stable, machine-readable error classification carried in every error response. Clients branch on code; message is for humans and may change at any time.

UserPermission + ALL_USER_PERMISSIONSone catalogue of permissions for the whole platform, not one per service. iveri-identity-api stores members on role rows, resolves them into access tokens and validates custom-role input against ALL_USER_PERMISSIONS; every other service reads them back out of the token to authorize a request; the frontend's <RequirePermission> branches on the same values. A service defining its own enum locally would find its permissions unassignable, because identity would reject them as unknown.

Values are <resource>:<action>, or <service>:<resource>:<action> where a resource name would otherwise collide — identity's members are unprefixed because they predate the shared catalogue and are already embedded in issued tokens and stored role rows.

api/identity/@iveri/contracts/identity

iveri-identity-api's authentication surface: SignupBody, LoginBody, RefreshTokenBody, AcceptInvitationBody, AuthTokens, ServiceToken, Principal, AuthSession, plus TenantStatus, MembershipStatus and PrincipalType.

It also holds AccessTokenPayload — the claims inside the JWT itself, rather than a request or response body. It belongs here for a stronger reason than the rest: identity is the only service that issues a token and every other service verifies one, so both sides must agree claim for claim. conduit-api kept a hand-copied duplicate until the pt claim had to be added to both at once — the drift a copy invites, arriving exactly when it is most expensive.

Auth only. Identity also exposes tenant, user, membership, role and API-key routes; those have one consumer each and stay in the service until a second one appears.

api/conduit/@iveri/contracts/conduit

conduit-api's full admin surface.

Inspect mode: Endpoint, CaptureSummary/CaptureDetail, Provider with its SignatureManifest/HandshakeManifest, Replay, SignatureDiagnostic.

Gateway mode: Destination, Route with its HeaderMatcher/BodyMatcher, Delivery and DeliveryAttempt.

Plus the request bodies for each, and the seven enums they are built from — SignatureVerdict, SignatureAlgorithm, SignatureEncoding, SignatureHeaderScheme, HandshakeKind, MatchOperator, DeliveryStatus.

api/unibox/@iveri/contracts/unibox

unibox-api's surface: Channel and its ChannelIngestSecretResponse, Contact with its ContactIdentity, Conversation/ConversationWithMessages, Message with MessageAttachment, Team, CannedReply, plus the request bodies and six enums — ChannelPlatform, ConversationStatus, MessageDirection, MessageAuthorType, MessageStatus, MessageContentType.

api/unibox-ai/@iveri/contracts/unibox-ai

The authenticated agent-runtime boundary: bounded conversation context, the handoff-aware agent run request, its acceptance statuses and response, and safe tenant MCP server metadata and registration request bodies. Credential values are request-only and never appear in responses.

Two of those enums carry members nothing writes yet, and both are deliberate rather than speculative. MessageAuthorType.AI exists before unibox-ai does, because it is what an agent taking over reads to know what the customer was already told — adding it later would leave every message written before then indistinguishable from a human's, retroactively. ChannelPlatform.ECHO is a first-class member for the same reason Conduit's local-echo connector is: the whole pipeline is provable without a Meta app, and a test double would put a branch in the normalizer registry that only exists under Jest.

api/localization/@iveri/contracts/localization

iveri-localization-api's surface, and the clearest case yet for a shared entry point: the two halves are consumed by different kinds of client and must not drift. TranslationBundle is what a frontend build pulls and commits; RenderBody/RenderedMessage is what a backend posts when it needs a string in the recipient's language. Locale, Namespace, Message with its MessageVariable declarations, Translation, Release and ReleaseDiff carry the authoring surface the translator panel is written against, with TranslationStatus and VariableType.

Locale is a row rather than an enum on purpose — see the type's own note. Nothing in the platform branches on which language a string is in, so encoding the set in a type would make adding one a release of this package and a redeploy of every consumer, for a value no code reads.

api/notification/@iveri/contracts/notification

iveri-notification-api's surface: NotificationTemplate and its upsert body, Notification with its per-channel NotificationDelivery list, SubmitNotificationBody, InAppNotification, and the NotificationChannel and NotificationDeliveryStatus enums.

Three shapes here are decisions rather than descriptions, and each is pinned by a spec:

  • Status lives on the delivery, not the notification. One that emailed successfully and dead-lettered in the inbox has two true answers, and a rolled-up field would have to pick one — hiding a dead letter behind a success, or failing a notification that mostly worked.
  • No push channel, because there is no device registry, no per-platform credentials and no token rotation behind one. A channel a caller can name and nothing can deliver looks like a successful submit and reaches nobody.
  • No delivered status, because a relay accepting a message is not evidence a person received it and nothing subscribes to a bounce yet. SENT is the strongest honest statement.

A template holds message keys, never text — the wording lives in iveri-localization-api, so there is one place with ICU validation, a review gate and Georgian, rather than two.

Why the wire shapes moved here

conduit-admin-web hand-transcribed all of it, carefully and with comments. It still drifted: TenantStatus was written 'active' | 'suspended' | 'cancelled' where identity sends 'ACTIVE' | 'SUSPENDED' | 'PENDING_DELETION' — wrong casing, and a member that has never existed — and bodySha256 was missing from two shapes. Nothing caught any of it, because a hand-written mirror is checked by nobody.

Both sides now compile against these types: each service's response DTO implements the interface it fulfils, so a field renamed in conduit-api fails tsc in conduit-api rather than surfacing as undefined in a panel weeks later.

What is deliberately not here yet

event/ — the domain event envelope and topic map. No service publishes one yet; it lands with Conduit's outbox.

Adding to the contract

  • A new ErrorCode or UserPermission member is a minor version. Changing what an existing member means, or removing one, is major — clients branch on these, and a removed permission silently downgrades every live session whose token still carries it.
  • Version 0.9.0 adds the first Commerce permissions: commerce:catalog:read and commerce:catalog:manage.
  • Version 0.10.0 adds the Unibox AI agent-run entry point and unibox:ai:run permission.
  • Version 0.11.0 adds safe MCP server metadata, registration request bodies, and the unibox:mcp:read / unibox:mcp:manage permissions.
  • Version 0.12.0 adds the billing plan, subscription, usage and payment permissions.
  • Version 0.13.0 adds the billing invoice read and manage permissions.
  • Version 0.14.0 adds messaging message, OTP and credit permissions.
  • Version 0.15.0 adds optional per-key FIFO ordering to outbound dispatches.
  • Version 0.18.0 adds the localization entry point and the localization:read, localization:message:manage, localization:translate, localization:publish and localization:render permissions.
  • Widening a type is minor; narrowing it is major.
  • Under api/, the contract follows the service: add the field to the service's DTO and to the interface in the same release, and let implements prove they agree.
  • Breaking changes get a migration note in CHANGELOG.md. Services upgrade deliberately.

Build output

Dual ESM + CJS (dist/esm, dist/cjs) behind an exports map, so NestJS requires it and Vite tree-shakes it. sideEffects: false.

The subpath entry points also need a typesVersions block, because TypeScript only reads exports under moduleResolution node16/nodenext/bundler and every Nest service in the fleet compiles with "node". Without it @iveri/contracts/conduit resolves at runtime and is untyped at compile time — in one repo but not another, which is a confusing thing to debug. scripts/verify-entry-points.mjs runs in build and fails if either half is missing.