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

@m4ike1/ion-server

v0.1.1

Published

experimental server package for ion

Readme

@m4ike1/ion-server

Experimental local server for the new durable Session and Agent Harness interfaces.

Server binds one logical serverId to one or more ServerListener transports, runs the version handshake, and routes opaque service calls to server-scoped and Session-scoped providers. It never decodes business payloads: Chord owns service-control parsing, error codes, snapshots and updates; @m4ike1/ion-protocol owns envelope validation, CBOR, and framing. The real Session and AgentHarness stay process-local; clients only ever hold presentation attachments.

import { randomUUID } from "node:crypto";
import { MemorySessionRepo, type Session } from "@m4ike1/ion-agent-core";
import {
  type RoutedServerServiceHost,
  type RoutedSessionHandle,
  type ServerHost,
  SessionAmbiguousError,
  SessionNotFoundError,
} from "@m4ike1/ion-server";
import { createUnixServer, getUnixSocketPath } from "@m4ike1/ion-server/unix";

async function startServer(
  serverServices: RoutedServerServiceHost,
  openRoutedSession: (session: Session) => Promise<RoutedSessionHandle>,
) {
  const sessions = new MemorySessionRepo();
  const host: ServerHost = {
    serverServices,
    async resolveSession(sessionId, context) {
      const matches = (await sessions.list(undefined, context))
        .filter((metadata) => metadata.id === sessionId);
      if (matches.length === 0) {
        throw new SessionNotFoundError(`Unknown session: ${sessionId}`);
      }
      if (matches.length > 1) throw new SessionAmbiguousError();
      return matches[0];
    },
    async openSession(metadata, context) {
      const session = await sessions.open(metadata, context);
      try {
        return await openRoutedSession(session);
      } catch (error) {
        try {
          await session.close(context);
        } catch (cleanupError) {
          throw new AggregateError(
            [error, cleanupError],
            "Harness creation and Session cleanup failed",
          );
        }
        throw error;
      }
    },
  };

  const serverId = randomUUID();
  const server = createUnixServer(host, {
    serverId,
    path: getUnixSocketPath(serverId, "/run/user/1000/ion"),
  });
  await server.start();
  return server;
}

How it works

Three application-owned capabilities plug into the router:

  • serverServices: RoutedServerServiceHost — creates one connection-scoped server service endpoint per client via attachClient(). Its RoutedServerPresentation argument is how server service implementations drive Session routing (attachSession, detachSession, prepareSessionRemoval).
  • resolveSession(sessionId, context) — maps a durable Session ID to metadata, or throws a bounded routing error (SessionNotFoundError, SessionAmbiguousError). Session discovery and management are application-owned services; the server only asks the resolver when routing an attachment.
  • openSession(metadata, context) — acquires the worker-local Session and returns a RoutedSessionHandle. Failures are cleaned up in that worker.

Routing is opaque end to end:

  • Server service calls and subscriptions route through the connection's RoutedServerServiceAttachment; Session calls route through RoutedSessionHandle.attachClient() presentation attachments via invokeService(), which forwards the service/member envelope without server-side business-payload decoding.
  • A Session may have multiple presentation attachments. Repeating attach from one connection is idempotent. Every successful attachment gets a server-generated attachmentId delivered only as routing control data in an out-of-band attachment message.
  • Session requests carry { serverId, sessionId, attachmentId }; the server rejects stale or mismatched routes with SessionNotAttachedError and wrong-server targets with WrongServerError.
  • Subscription snapshots are encoded per connection; updates stay scoped to the requesting attachment. Application observations such as transcripts route as ordinary service state without server-owned business schemas.

Lifecycle

  • new Server(host, options) validates options eagerly (serverId must be a canonical lowercase UUIDv4). start() starts each listener in order; a listener failure closes the listeners that already started and rejects closed.
  • close() stops listeners, closes connections, waits for admitted service calls to settle, releases attachments, then closes routed Session handles. closed resolves after shutdown or rejects when listener or Session cleanup fails.
  • Losing a connection rejects its local in-flight responses but releases its attachment only after admitted service calls settle. Disconnecting never deletes the hosted Session; the host decides when zero presentation demand and worker-local Harness activity permit worker retirement.
  • While draining, new attachments and Session-targeted calls fail with ServerDrainingError. Server.close() aggregates listener and Session cleanup failures into AggregateError.
  • Per-connection handshake: the first client message must be hello with a supported protocol version, else the connection is failed with hello_error. Handshakes time out after handshakeTimeoutMs (default 5,000 ms). Duplicate request IDs and duplicate subscription IDs are rejected per connection; cancel envelopes abort the matching in-flight request.
  • RoutedSessionHandle.terminated lets the router drop a hosted Session whose worker died unexpectedly; the next attach re-opens it through the host.

Transports

serverId is a logical identity supplied by the launcher, not a socket address. Server composes transports through ServerListener; peer authentication remains application policy and is not implemented by the experimental Unix transport.

The @m4ike1/ion-server/unix submodule provides createUnixListener() and createUnixServer(). The Unix preset requires an explicit physical path; getUnixSocketPath() derives one from a caller-selected directory. Choose a short, private runtime directory rather than deriving the route from an unbounded home-directory path. A long-lived launcher can reuse the same ID and path when replacing a server process. Stale socket files are reclaimed only after probing that no live server owns them; non-socket paths are never removed.

Error model

Errors that cross the protocol boundary are ServerError subclasses with stable codes (wrong_server, session_not_found, session_ambiguous, session_not_attached, server_draining, plus Chord remote-service codes). Anything else is sanitized to internal_error without exposing private details; the original is forwarded to onError. Error observers never affect server state.

Testing

@m4ike1/ion-server/testing provides createTestServer() (unstarted Server with deterministic defaults), TestServerHost/TestHarness (in-memory host with gateable open/service/close paths), and ProtocolTestClient/connectUnixTestClient() wire clients for transport conformance tests. See docs/reference.md and docs/setup.md.