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

@objectstack/service-realtime

v17.2.0

Published

Realtime Service for ObjectStack — implements IRealtimeService with WebSocket and in-memory pub/sub

Readme

@objectstack/service-realtime

In-process pub/sub for ObjectStack — the production IRealtimeService implementation, backed by an in-memory adapter.

⚠️ Server-internal only — no client transport. This service delivers events to trusted, in-process subscribers (today: the webhook auto-enqueuer and knowledge sync). There is no WebSocket/SSE endpoint, no REST subscribe route, and no working client transportIRealtimeService.handleUpgrade is deliberately unimplemented platform-wide, and the @objectstack/client RealtimeAPI is a placeholder. See Security posture before changing that.

What this package actually provides

  • InMemoryRealtimeAdapter — a Map-backed pub/sub implementing IRealtimeService (publish / subscribe / unsubscribe), with:
    • channel-based routing and per-subscription filtering by object name and event types (RealtimeSubscriptionOptions.object / eventTypes; options.filter is declared in the contract but not evaluated);
    • a maxSubscriptions safety cap (default 50 000; 0 = unbounded, tests only) so a subscription leak can't grow the map until the pod OOMs;
    • handler errors swallowed per-delivery so one bad subscriber can't break the publish loop.
  • RealtimeServicePlugin — registers the adapter as the kernel realtime service, registers the sys_presence system object, and contributes its translations.

v1 deployment contract (launch-readiness P0-5): single-instance only. The adapter is process-local — events published on node A are not delivered to subscribers on node B. An HA adapter (Redis-backed, over service-cluster-redis) is a post-GA fast-follow.

Usage (server-side, trusted code only)

import { ObjectKernel } from '@objectstack/core';
import { RealtimeServicePlugin } from '@objectstack/service-realtime';

const kernel = new ObjectKernel();
kernel.use(new RealtimeServicePlugin());
await kernel.bootstrap();

const realtime = kernel.getService('realtime');

const subId = await realtime.subscribe('records', (event) => {
  console.log(event.type, event.payload);
}, { object: 'account', eventTypes: ['record.created'] });

await realtime.publish({
  type: 'record.created',
  object: 'account',
  payload: { id: 'acc-1', name: 'Acme' },
  timestamp: new Date().toISOString(),
});

await realtime.unsubscribe(subId);

Configuration:

new RealtimeServicePlugin({
  adapter: 'memory',                    // only supported adapter today
  memory: { maxSubscriptions: 50_000 }, // 0 = unbounded (tests only)
});

Security posture (#2992 / ADR-0096 D4)

Delivery is a pure fan-out with no per-recipient authorization seam:

  • subscriptions carry no principal — there is nothing to check a row against;
  • matchesSubscription filters only by object name + event type;
  • the ObjectQL engine publishes record.created / record.updated events with the full record body (the after row) — rows and fields a subscriber's own find would hide under RLS/FLS/tenant scoping.

That is safe only while every subscriber is trusted server-internal code. Before any end-user transport ships (WebSocket handleUpgrade, SSE, a REST subscribe route, or a real client transport), the delivery path MUST gain one of:

  1. a per-recipient re-check on delivery — the subscription carries the subscriber's ExecutionContext, and every event is re-authorized (RLS/FLS/tenant) against it before the handler fires; or
  2. id-only payloads — the client re-fetches the record under its own authority.

This posture is registered in the authz conformance matrix (packages/qa/dogfood/test/authz-conformance.matrix.ts, row realtime-delivery-authz), and transport tripwire probes in authz-conformance.test.ts fail CI if a transport is wired without upgrading that row with a real enforcement site.

Contract

Implements IRealtimeService from @objectstack/spec/contracts:

interface IRealtimeService {
  publish(event: RealtimeEventPayload): Promise<void>;
  subscribe(channel: string, handler: RealtimeEventHandler, options?: RealtimeSubscriptionOptions): Promise<string>;
  unsubscribe(subscriptionId: string): Promise<void>;
  handleUpgrade?(request: Request): Promise<Response>;   // deliberately unimplemented — see above
  subscribeMetadata?(filter, handler): Promise<string>;  // optional convenience — not implemented here
  subscribeData?(filter, handler): Promise<string>;      // optional convenience — not implemented here
}

License

Apache-2.0. See LICENSING.md.

See Also